Unit Testing Phoenix Controllers with Mox

As the ecosystem for Elixir matures more and more, there are some libraries that seem particularly promising to me. One of them is Mox, a simple but powerful library for implementing mocks for predefined behaviours (note the British spelling!). José Valim started developing it only a few months ago, but I’ve already found it to be a very useful and flexible tool for writing tests.

Because Mox is so new, I haven’t been able to find many tutorials or guides for it. However, since it’s so easy to set up a basic mock, I thought it would be worthwhile to show how I’m using this library for a particular scenario: unit testing Phoenix controllers. I’ll first give an overview of Mox and why it’s appropriate for the task, and then I’ll walk through an actual implementation.

The Benefits of Mox

The philosophy behind Mox can be found in an article José wrote in 2010. A simple summary is that when it comes to dependency injection, mocks should not be created ad-hoc. Instead, they should be constrained by predefined behaviours. This helps enforce contracts between modules, and it also makes tests easier to maintain and understand.

Mox ties mock definitions to specific behaviours. It also requires that any stubbed functions are callbacks for that behaviour. But this library isn’t all about constraints–different implementations of mocks can be run concurrently, which is fantastic for tests. It’s also very easy to customize implementations in each test. Try to keep all of this in mind as you look over the walkthrough below.

Unit Testing a Phoenix Controller

All of the following assumes you’re using Elixir 1.4 and Phoenix 1.3.

  1. Run mix phx.new mox-guide
  2. Add {:mox, "~> 0.3.1"} to mix.exs, and run mix deps.get
  3. Add a configuration for a service to be used by the controller (which is coming up soon):
    config :mox_guide,
      user_service: MoxGuide.UserService

    Note that we’re not actually going to build this module, but luckily, we don’t need one for a clean compilation!

  4. Override that config in config/test.exs:
    config :mox_guide,
      user_service: MoxGuideWeb.UserServiceMock

    This is the module we’ll be using as a mock.

  5. Add the following to test_helper.exs:
    Mox.defmock(MoxGuideWeb.UserServiceMock, for: MoxGuideWeb.UserServiceBehaviour)
  6. Now add an endpoint to router.ex (inside the existing “/” scope):
    post "/register", UserController, :register


Now, here are the three new files we need:


First of all, a module that defines the behaviour (I put mine in a directory reserved for service behaviours):

defmodule MoxGuideWeb.UserServiceBehaviour do
 @type user :: map
 @type error :: :invalid_params

 @doc """
 Registers a User with information about their credentials, preferences, etc.
 @callback register_user(user_params :: map) :: {:ok, user} | {:error, error}


Notice that, for the sake of simplicity, we’re just responding with plain JSON instead of going through a view.

defmodule MoxGuideWeb.UserController do
 use MoxGuideWeb, :controller

 @user_service Application.get_env(:mox_guide, :user_service)

 def register(conn, %{"user" => _} = params) do
  with {:ok, user} <- @user_service.register_user(params) do 
   |> put_status(:ok)
   |> json(user)
   {:error, :missing_param} ->
    |> put_status(:unprocessable_entity)
    |> json(%{error: "invalid parameters"})
   {:error, :invalid_param} ->
    |> put_status(:unprocessable_entity)
    |> json(%{error: "invalid parameters"})


defmodule MoxGuideWeb.UserControllerTest do
  import Mox

  setup do
   base_params = %{user: %{name: "Chuck Testa"}}
   {:ok, params: base_params}

  describe "REGISTER" do
   setup :verify_on_exit!

   test "valid parameters yield OK response and body", %{conn: conn, params: params} do
    expect(@service_mock, :register_user, fn _ -> {:ok, %{name: "Chuck Testa"}} end)
    conn = post conn, user_path(conn, :register), Map.to_list(params)
    assert json_response(conn, :ok) == %{"name" => "Chuck Testa"}

   test "missing parameters yield 422 with error message", %{conn: conn, params: params} do
    expect(@service_mock, :register_user, 2, fn
      ^params ->
     {:ok, raise "We don't want this function to succeed!"}
     {:error, :invalid_param}

    error_resp = %{"error" => "invalid parameters"}
    # Missing embedded parameter
    user_params = put_in(params.user.name, nil)
    conn = post conn, user_path(conn, :register), user_params
    assert json_response(conn, :unprocessable_entity) == error_resp

    # Missing top-level parameter
    user_params = put_in(params.user, nil)
    conn = post conn, user_path(conn, :register), user_params
    assert json_response(conn, :unprocessable_entity) == error_resp

   test "invalid name yields same response as missing name", %{conn: conn, params: params} do
    expect(@service_mock, :register_user, fn _ -> {:error, :invalid_param} end)
    conn2 = post conn, user_path(conn, :register), Map.to_list(params)
    expect(@service_mock, :register_user, fn _ -> {:error, :missing_param} end)
    conn = post conn, user_path(conn, :register), Map.to_list(params)
    assert response(conn, :unprocessable_entity) == response(conn2, :unprocessable_entity)

Now just run mix test, and everything should pass.

How Mox is Working Here

See how easy the setup on Line 16 of user_controller_test is? Since we really want to test how the controller handles output from the service module, the input into the service module doesn’t matter. In this test, we know exactly what the controller will get back from the service, which lets us easily test out the response data in Line 20.

Line 24 shows how to expect a specific number of calls to register_user/2. You can also use stub/3 to circumvent any expectation validation. This expect is also a good example of how useful pattern matching is here. There’s one clause for a  “good” call to register_user/2 (which we don’t want to call), and one clause for all others. You could even stub a single implementation of register_user/2 at the top-level setup macro that just has one clause for each test scenario–that’s what I was doing for a while, but I decided it’s more readable to define expectations within each test.

You can also redefine expectations within a test, as on Line 50. While the exact placement of expectations is unfortunately not very strict, multiple definitions are useful if you want the same input (or in this case, any input with arity of 1) to have different outputs throughout the tests. Any calls to register_user/2 after the second definition will behave as if the first one never existed.

Going Forward

Once again, the best part of Mox is that it’s not only simple and flexible, but it also encourages best practices like explicitness and binding injected dependencies to contracts (i.e. behaviours). There are also a lot of nice tricks that aren’t utilized in the sample test here, such as replacing anonymous functions inside of expectations with references to other functions, like so:

expect(@service_mock, :register_user, &some_function/1)

This can boost readability and also supports the DRY principle. In addition, Mox has some features to support multi-process collaboration, if your situation requires it.

I don’t yet know if Mox would be quite as powerful when testing other types of modules like GenServers, but it’s been a pleasure to use for Phoenix controllers, and it’ll probably remain my go-to library for mocks in Elixir.