www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Monkey Patching in D

reply "Peter Lundgren" <peter peterlundgren.com> writes:
I'm interested in trying to write a monkey patching* library in 
D. Specifically, aiming to support mocks and stubs for unit 
testing.

I have a couple of questions:

1) What prior art is there here? I am aware of DMocks (does this 
project still need a maintainer?).
2) How crazy am I for even thinking this is a good idea? Are 
there any obviously better alternatives.

* I'm not sure if there is well defined terminology here 
(especially applying these ideas to compiled languages). To 
clarify, when I say monkey patch, I mean to intercept a function 
or method call at run-time. I believe, in the case of D, this 
would require self modifying code using techniques like those 
used here: 
http://www.yosefk.com/blog/machine-code-monkey-patching.html
Jun 04 2013
next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, June 05, 2013 04:18:34 Peter Lundgren wrote:
 I'm interested in trying to write a monkey patching* library in
 D. Specifically, aiming to support mocks and stubs for unit
 testing.
 
 I have a couple of questions:
 
 1) What prior art is there here? I am aware of DMocks (does this
 project still need a maintainer?).
 2) How crazy am I for even thinking this is a good idea? Are
 there any obviously better alternatives.
 
 * I'm not sure if there is well defined terminology here
 (especially applying these ideas to compiled languages). To
 clarify, when I say monkey patch, I mean to intercept a function
 or method call at run-time. I believe, in the case of D, this
 would require self modifying code using techniques like those
 used here:
 http://www.yosefk.com/blog/machine-code-monkey-patching.html

If you haven't watched it yet, you should watch this talk from dconf. It's specifically about stuff like mock testing in D. http://www.youtube.com/watch?v=V98Z11V7kEY - Jonathan M Davis
Jun 04 2013
prev sibling next sibling parent "Peter Lundgren" <peter peterlundgren.com> writes:
 If you haven't watched it yet, you should watch this talk from 
 dconf. It's
 specifically about stuff like mock testing in D.

 http://www.youtube.com/watch?v=V98Z11V7kEY

 - Jonathan M Davis

Yeah, deject looks pretty nice, but only works on code you own. It's certainly a much safer alternative to what I'm considering.
Jun 04 2013
prev sibling next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 06/04/2013 07:18 PM, Peter Lundgren wrote:

 I am aware of DMocks (does this project
 still need a maintainer?).

The maintainer has just announced that it is up to date with the recent compiler: http://forum.dlang.org/post/dnvfxgvecmskjofjryfj forum.dlang.org Ali
Jun 04 2013
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-06-05 04:18, Peter Lundgren wrote:
 I'm interested in trying to write a monkey patching* library in D.
 Specifically, aiming to support mocks and stubs for unit testing.

 I have a couple of questions:

 1) What prior art is there here? I am aware of DMocks (does this project
 still need a maintainer?).
 2) How crazy am I for even thinking this is a good idea? Are there any
 obviously better alternatives.

 * I'm not sure if there is well defined terminology here (especially
 applying these ideas to compiled languages). To clarify, when I say
 monkey patch, I mean to intercept a function or method call at run-time.
 I believe, in the case of D, this would require self modifying code
 using techniques like those used here:
 http://www.yosefk.com/blog/machine-code-monkey-patching.html

I would suggestion using some kind of wrapper instead of doing true monkey patching. Example: class Wrapper { private Object o; this (Object o) { this.o = o; } auto opDispatch (string name, Args ...) (Args args) { // Use this function to "catch" all calls and forward as necessary to "o" } } http://dlang.org/operatoroverloading.html#Dispatch -- /Jacob Carlborg
Jun 05 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-06-06 14:53, Peter Lundgren wrote:

 I came to the same conclusion, so that's exactly what I'm proposing /
 asking for feedback on. Do you think that it would be reasonable to
 apply those techniques to D?

Have a look at this old D1 library: http://flectioned.kuehne.cn/ It allows you to redirect a function. -- /Jacob Carlborg
Jun 09 2013
prev sibling next sibling parent "Peter Lundgren" <peter peterlundgren.com> writes:
On Wednesday, 5 June 2013 at 07:17:50 UTC, Jacob Carlborg wrote:
 I would suggestion using some kind of wrapper instead of doing 
 true monkey patching. Example:

 class Wrapper
 {
     private Object o;
     this (Object o) { this.o = o; }

     auto opDispatch (string name, Args ...) (Args args)
     {
         // Use this function to "catch" all calls and forward 
 as necessary to "o"
     }
 }

 http://dlang.org/operatoroverloading.html#Dispatch

That's a reasonable option too. I think I'd rather use deject. The problem I'm trying to solve (and maybe this isn't as important as I think) is that DMocks or deject + DMocks only works on a subset of the language. I can't mock out free function calls, private or final methods, or functions on structs or unions (can I inject mocked structs using deject?). What do I do when I want to mock out std.stdio or std.random?
Jun 05 2013
prev sibling next sibling parent "QAston" <qaston gmail.com> writes:
On Wednesday, 5 June 2013 at 13:19:33 UTC, Peter Lundgren wrote:
 On Wednesday, 5 June 2013 at 07:17:50 UTC, Jacob Carlborg wrote:
 I would suggestion using some kind of wrapper instead of doing 
 true monkey patching. Example:

 class Wrapper
 {
    private Object o;
    this (Object o) { this.o = o; }

    auto opDispatch (string name, Args ...) (Args args)
    {
        // Use this function to "catch" all calls and forward 
 as necessary to "o"
    }
 }

 http://dlang.org/operatoroverloading.html#Dispatch

That's a reasonable option too. I think I'd rather use deject. The problem I'm trying to solve (and maybe this isn't as important as I think) is that DMocks or deject + DMocks only works on a subset of the language. I can't mock out free function calls, private or final methods, or functions on structs or unions (can I inject mocked structs using deject?). What do I do when I want to mock out std.stdio or std.random?

Making mocks work with statical world of D could definitely be done - the language has many useful tools like alias or mixins to do that. I think that substitution here may be a problem. It'd probably be required to use aliases instead of bare types in user code, so types could be fully substituted. Mocking statically linked methods is a similar problem - consider: MyType a = new Mocker().mock!(MyType ); a.finalMethod() will call MyType.finalMethod, not Mock!MyType.finalMethod. Using aliases for defining types is a sollution but it requires changes to user code. Maybe deject has a sollution to this problem.
Jun 05 2013
prev sibling next sibling parent "Paulo Pinto" <pjmlp progtools.org> writes:
On Wednesday, 5 June 2013 at 13:19:33 UTC, Peter Lundgren wrote:
 On Wednesday, 5 June 2013 at 07:17:50 UTC, Jacob Carlborg wrote:
 I would suggestion using some kind of wrapper instead of doing 
 true monkey patching. Example:

 class Wrapper
 {
    private Object o;
    this (Object o) { this.o = o; }

    auto opDispatch (string name, Args ...) (Args args)
    {
        // Use this function to "catch" all calls and forward 
 as necessary to "o"
    }
 }

 http://dlang.org/operatoroverloading.html#Dispatch

That's a reasonable option too. I think I'd rather use deject. The problem I'm trying to solve (and maybe this isn't as important as I think) is that DMocks or deject + DMocks only works on a subset of the language. I can't mock out free function calls, private or final methods, or functions on structs or unions (can I inject mocked structs using deject?). What do I do when I want to mock out std.stdio or std.random?

The solution would be to use tools like in C++/Java/.NET world that rewrite assembly/bytecode and replace method/function calls with your mocks. It works most of the time. -- Paulo
Jun 06 2013
prev sibling next sibling parent "Peter Lundgren" <peter peterlundgren.com> writes:
On Thursday, 6 June 2013 at 07:46:13 UTC, Paulo Pinto wrote:
 The solution would be to use tools like in C++/Java/.NET world 
 that rewrite assembly/bytecode and replace method/function 
 calls with your mocks.

 It works most of the time.

 --
 Paulo

I came to the same conclusion, so that's exactly what I'm proposing / asking for feedback on. Do you think that it would be reasonable to apply those techniques to D?
Jun 06 2013
prev sibling parent "Dicebot" <m.strashun gmail.com> writes:
On Wednesday, 5 June 2013 at 13:19:33 UTC, Peter Lundgren wrote:
 The problem I'm trying to solve (and maybe this isn't as 
 important as I think) is that DMocks or deject + DMocks only 
 works on a subset of the language. I can't mock out free 
 function calls, private or final methods, or functions on 
 structs or unions (can I inject mocked structs using deject?).

As I have mentioned in reddit comment to that presentation, you kind of can achieve same dependency substitution by generating whole mock modules and changing root import path via flag for test run. As module is minimal D unit of encapsulation, that is even more natural. However, I don't know if it is possible to somehow get dependency list automatically in such model. Probably by grep'ing linker errors and doing demangling?
Jun 07 2013