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.
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,
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 creates a new mocked module based on what you pass in. It has a
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
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.
:meck.unload feel like boilerplate to me and should probably
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.
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.