TIL: How to test GenServers with Mox

Come for the explainer, stay for the poetry

Meryl Dakin
Flatiron Labs

--

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!

Footer top

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.

Footer bottom

--

--

Meryl Dakin
Flatiron Labs

Dev at Knock.app. @meryldakin on github, LinkedIn, and twitter.