Is Mocking discouraged? I would like to isolate testing

EDIT: SimpleMock.jl allows mocking without needing macros in the live code, unlike Mocking.jl

Let’s say I have 2 modules (e.g. below) and I want to test out ModuleB in an isolated way; i.e. without running the code in ModuleA.

Is there a way to mock out or patch ModuleA when I unit test ModuleB ? If not, what are some alternative testing strategies I could use?

module ModuleA 
    function f_a()
        println(1)
    end
end
module ModuleB
    import Main: ModuleA
    function g()
        ModuleA.f_a()
    end
end

I’m coming from Python, where it’s common to mock calls from other files and instead just assert that the other file was called, instead of actually calling it.

I have never seen it used, but making an object that spits out mock functions is not hard.

struct Mock end
Base.getproperty(::Mock, s::Symbol) = (args...) -> println("Mock call to ", s, "(", join(repr.(args), ", "), ")")

const ModuleA = Mock()
ModuleA.f_a()
1 Like

May be you need something like this : GitHub - invenia/Mocking.jl: Allows Julia function calls to be temporarily overloaded for purpose of testing

2 Likes

There are several options for Julia mocking libraries:

https://github.com/invenia/Mocking.jl

https://github.com/JuliaTesting/SimpleMock.jl

https://github.com/tk3369/Pretend.jl

5 Likes

I would avoid mocking whenever possible. Design your program in a functional style so that any I/O is on the outside and you can test the body of your program without substituting anything. When you need to do I/O to test against a web service, try to use the actual web service’s test endpoint unless it’s too slow. At that point you can make a fake FakeClient that behaves just like RealClient but faster – still saving data, just in memory instead of over the net. Not tracking method calls like some mocks would.

7 Likes

I’m doing something like this:

function _input_f()
    return data
end

function _output_f(data)
end

function logic(
    p1, p2;
    inpit_data = _input_f(),
    output_f = _output_f()
)
    result = do_stuff_without_side_effects(p1, p2, inpit_data)
    output_f(result)
end

That’s very easy to test. Are there any disadvantage in that style?

Thanks! It looks like SimpleMock provides the cleanest tests since it doesn’t require modifications to the live code

This would redefine ModuleA, and I would also want to test the un-replaced ModuleA at some point

Hello from the future, where this discussion seems to be the #1 Google result for “julia test mock”.

For the three libraries mentioned by @tk3369 above, it looks like only Mocking.jl remains at least a semi-active project. SimpleMock.jl’s README says it’s broken for modern versions of Julia, and Pretend.jl looks like it hasn’t been touched in four years, though that doesn’t necessarily imply there’s anything wrong with it.

Also:

Not all mocking is for network isolation purposes. For me, the biggest use case is something like “conditional verification”, where I want to test ModuleB, which internally calls ModuleA, under the conditional assumption that ModuleA is known to work properly, and/or returns a given result quickly.

In many cases I agree that it’s inappropriate to care how ModuleB achieves its results, it’s a black box, and the fact that it uses ModuleA internally is an implementation detail. But in other cases, for lots of different reasons, it’s very helpful to call that a “grey box” that can be marginalized over for testing purposes.

2 Likes

Just some thoughts …

  • If ModuleB relies on ModuleA it also relies on the correctness of it, i.e., conditional testing might debugging easier – because you know where to look for the failure – but is not enough in its own.

  • If the coupling between ModuleB and ModuleA is not as strong in the first place, i.e., it just happens to use some functions from ModuleA, but could just as well work with a different backend, it should probably restructured to rely on an abstract interface instead. Julia is especially nice in this respect and as soon as an abstract interface has been identified mocking becomes trivial – by just implementing some mock code adhering to the interface.

1 Like

There’s not just one right answer. Sometimes I need to test ModuleB end-to-end, sometimes I need to test it conditionally on what ModuleA is providing, sometimes both.