Is monkey patching possible in julia 0.5?

testing

#1

Is there any means to (temporarily) redefine a function, so that:

f = function(x) 0.5 end
# monkey patch Base.sin with f
sin(12.0)  # returns 0.5
# undo monkey patching

I suppose it was once possible, done like here: https://github.com/burrowsa/Fixtures.jl/blob/master/src/mock/patch.jl#L23

Now, doing something similar is not possible anymore, since sin has no fields like code, fptr, env, which I assume defined the function back in 0.3.

I need such thing to temporarily override any function’s behavior to isolate tests from effects of stuff like Base.open etc.

One vague idea I have would be to redefine a function for particular argument types i.e. import Base.sin, redefine sin(::Float64), test, undo (somehow). But that seems to require much more effort - to get the types right for the patch to work as intended. And it would produce redefinition warnings. Is there any other way?


#2

No and you shouldn’t do it.


#3

Probably not what you want, but in case your code happens to not depend on built-in calls to Base.sin, and if you recompile your own calls as needed, you could use sin as a variable:

f = function(x) 0.5 end
sin = f
sin(12.0) # returns 0.5
sin = Base.sin

#4

You can also define sin as a different function in a module or in a local scope.


#5

@akis, @StefanKarpinski - yes, but I think that doesn’t affect calls to sin done in other modules. In my case that would be modules under test, where I would like to monkey patch calls to functions that break my test isolation. sin is a silly example, sorry. More likely candidate is open or calls to an HTTP requests package. Like so:

# inside my tests:

fake_get = function(someargs) #=pretend to make an HTTP request=# end
Requests.get = fake_get
# test my code that uses Requests.get, but without any HTTP requests actually made
# ...
Requests.get = ?  # get the old Requests.get back

I am aware that this is by itself ugly, but how can you do a test as above and avoid monkey patching?


#6

No, that kind of monkey patching you cannot do. Testing is usually handled with some combination of mock objects and dependency injection.


#7

“Traditional” monkey patching as found in other dynamic languages isn’t directly supported by Julia, for performance reasons. Maybe you have to “plan ahead” and design your code to provide that kind of testing as an actual feature of your application (e.g. to maintain some state of whether your application is running in test mode and “consciously” call functions according to current mode, probably calling the fake ones with the invoke() function), instead of considering it as an external temporary change. That way, the resulting performance regression from not having the fake functions available at compile time will affect mostly the test mode.


#8

OK, thank you! So in this particular respect Julia is more “like Java” and “unlike Python”, all clear now.