TIL: How to test GenServers with Mox
Come for the explainer, stay for the poetry
We recently replaced a hand-rolled test controller with Mox for testing in our umbrella app (how and why are detailed in “Elixir Test Mocking with Mox”). Other than the numerous times we swore Mox wasn’t connected (but it was actually just our broken tests), there was one place in particular that stumped us: testing processes that involved GenServers.
The problem
In our app, we’re using GenServers to manage the transactional process of creating organizations and repositories on GitHub. They initiate separate processes for a number of these requests and clean up individual pieces if one of them fails.
When we run the tests, however, we get the following error message:
20:36:40.959 [error] GenServer #PID<0.930.0> terminating
** (Mox.UnexpectedCallError) no expectation defined for GithubClientMock.find_or_create_org/1 in process #PID<0.930.0>
The important bit in the error message is that it specifies the GenServer’s PID as the failing process. So we can see that the process itself doesn’t get access to the method we’re supplying with the mock.
The friction that we’re experiencing is fundamental in Mox. From the documentation:
“All expectations are defined based on the current process.”
Therefore, unless we grant it explicitly, our GenServer process won’t have access to Mox’s state of this process, which is comprised of the available functions.
The solution
Mox does provide for multiple processes to access mocks; it just doesn’t do so by default. There are two ways we can make these available: explicit allowances or by using the set_mox_global
macro in a setup block.
- Explicit allowances work by passing the mock to Mox’s
allow
function, giving access only to the child processes of the current one. - Using
set_mox_global
allows any child process to access the mocks and stubs we define for the current process.
Which one to use?
If you’ve configured your tests to run asynchronously, you’ll want to use allow
and grant explicit access to the child processes for your stubs. Keep in mind that you can only invoke :async
on your tests when they aren’t changing global state.
If your tests are not running in :async
mode, it’s safe to use the set_mox_global
macro, granting stub access to all your children.
Here are some docs on Mox!
No more blocks or locks
Or wasting tocks on clocks.
Testing quick like a fox –
Thanks to docs on Mox.— Natalie Perpepaj, software engineer and poet
Thanks for reading! Want to work on a mission-driven team that’s equally gifted at testing and poetry? We’re hiring!
To learn more about Flatiron School, visit the website, follow us on Facebook and Twitter, and visit us at upcoming events near you.
Flatiron School is a proud member of the WeWork family. Check out our sister technology blogs WeWork Technology and Making Meetup.