Here is a short article and video using meck in Elixir.
If you’re pretty comfortable with Elixir but are unfamiliar with meck, then the article should be enough.
Otherwise, the video might be useful. It’s one take, using my laptop microphone. My apologies for its roughness.
Easy Mocking in Elixir with Meck from Jim Whiteman on Vimeo.
You can grab the code from the video here.
Here is our hypothetical setup: a module named Math that supports addition, subtraction and multiplication.
Let’s pretend that multiplication is special and we have to introduce a dependency to get the job done.
Let’s also pretend that SomeDependency.mul
is pretty expensive to run.
We’ll imagine it makes a bunch of HTTP requests and then defrags a couple of hard disks for good measure.
It’s useful enough that we need it, but it’s a giant boat anchor weighing down
math_test.exs. Every time we hit multiplication in our test, SomeDependency.mul
comes along for the ride.
As a result, our math_test runs very slowly.
So, let’s bring :meck
into the mix to mock it out and bring sanity back to our tests.
To make things work we’ll make 3 invocations, :meck.new
, :meck.expect
and :meck.unload
.
:meck.new
creates a new mocked module based on what you pass in. It has a
decent
number of options on its own.
:meck.expect
is where the magic happens. You pass in the module and function
you wish to mock, and pass in the function you want to replace it with.
In the above test, I’m not really testing that multiplication works anymore, only that it hits some particular service named SomeDependency.
So instead of returning a calculation, I’m just sending a message back home with :called!
, and then asserting it ends up in our mailbox within a 100th of a second or so.
You could set this up a million different ways. There is nothing special about the symbol :called!, nor do you need to send a message back to yourself here. The replacement function can be whatever you want it to be. This is just what I chose for this example.
:meck.unload
unloads the module from memory, cleaning things up for the rest of
your tests.
And that’s that.
In the above test case, the real implementation of SomeDependency.mul
won’t be touched, and fn(2, 3) -> send home, :called! end
will be called instead.
Impressive!
:meck.new
and :meck.unload
feel like boilerplate to me and should probably
live in setup
and on_exit
, respectively.
Instead, though, let’s create an easy macro to take care of the setup and teardown for us on a per-test basis.
This turns our test case into this:
Not too shabby.
Actually, jjh42’s nice Elixir wrapper for mech does something similar. But it wasn’t too difficult to roll something simple for ourselves.
At any rate, there is a ton more you can do with Meck. If you’d like to use it, it would be a good use of your time to take a look at the project’s README.
The only thing I’d like to add is that I’m not entirely convinced that this is the Elixir way to do things. In particular, I don’t see it used too heavily out in the wild.
The advice I tend to hear instead is, “put as much of your logic as you can in stateless modules”. Maybe if 90% of your code is setup that way, then mocking in this manner becomes less of an issue. Or not. I’m still figuring this out myself.
Regardless, it’s a pretty slick library that feels comfortable, especially coming from Ruby.