---
title: Mocking and faking external dependencies in elixir tests
teaser: 'Explore a combination of mocking, faking, and dependency injection to focus
  on the code being tested.

  '
tags: testing,elixir
author: Wil Hall
published_on: 2019-09-23
---

During some recent work on thoughtbot's company announcement app [Constable], I
ran into a situation where I was introducing a new service object that made
external requests. When unit testing, it is easy enough to use some
straightforward mocks to avoid making external requests. However, for tests
unrelated to the service object, how can we mock out external requests without
littering those tests with explicit mocks?

## Unit testing using mocks

To set the stage, let's take a look at what a hypothetical service object that
makes an external request might look like:

```elixir
defmodule App.Services.WebService do
  def make_request do
    HTTPoison.get!("http://thoughtbot.com/")
  end
end
```

And if we were to write a unit test for this service object, we would want to
mock out the external request (in this case, the call to `HTTPoison.get!/1`). To
do that, we might use a library like [Mock]:

```elixir
defmodule App.Services.WebServiceTest do
  import Mock
  alias App.Services.WebService

  describe "#make_request" do
    test ". . ."
      # setup . . .

      get_mock = fn _url, _params, _headers ->
        %HTTPoison.Response{
          body: ". . .",
          status_code: 200
        }
      end

      response =
        Mock.with_mock HTTPoison, get!: get_mock do
          WebService.make_request()
        end

      # assertions . . .
    end
  end
end
```

## Where it gets tricky

Mocking is exactly what we want when unit testing the service object, but if we
have an unrelated unit tests that run code which happens to use our service
object, we want to ensure that no external requests are made when running our
test suite.

As an example, we might have a module that utilizes our service object:

```elixir
defmodule SomeModule
  alias App.Services.WebServiceTest

  def do_something
    . . .
    response = WebServiceTest.make_request()
    . . .
  end
end
```

If we were testing this object and in our test we called
`SomeModule.do_something/0`, we would inadvertently be making an external
request. It would be incorrect to mock `HTTPoison.get!/1` in this test because
that's an implementation detail of our service object. And while we could mock
`WebServiceTest.make_request/0`, that will lead to a lot of noise in our tests.

## Let's create a fake

One way we can get around this issue is to create a fake version of our service
object which has the same public interface, but returns fake data. That object
might look like:

```elixir
defmodule App.Services.FakeWebService do
  def make_request do
    %HTTPoison.Response{body: ". . .", status_code: 200}
  end
end
```

We want to utilize this fake by making our application code use `WebService`
unless we are testing, in which case we want to use `FakeWebService`.

A common way to accomplish this is to have three modules: `WebService`,
`WebServiceImplementation`, and `WebServiceFake`. Everyone calls methods on
`WebService` which then delegates to `WebServiceImplementation` when not
testing, or to `WebServiceFake` when testing. I don't particularly like this
pattern, because it requires an extra object and introduces complexity.

A much more simple and flexible solution is to use a form of
[dependency injection] where we dynamically refer to our service object which
is either the real service or the fake. There is a great elixir module called
[pact] which accomplishes this by creating a dependency registry which allows
us to define named dependencies, but conditionally switch out the actual value
they resolve to.

Using pact, we define a dependency registry for our application:

```elixir
defmodule App.Pact do
  use Pact
  alias App.Services.WebService

  register :web_service, WebService
end

App.Pact.start_link
```

And then we want to redefine that dependency to be our fake when we run our
tests. The following code, either in a test helper or in some setup that occurs
before all tests, will accomplish that:

```
App.Pact.register(:web_service, FakeWebService)
```

Finally, all calls to `WebService.make_request()` in our application and tests
become `App.Pact.get(:web_service).make_request()`. The one exception to this is
in our unit test for `WebService` itself - we want to test the actual service
object! So we should still explicitly call `WebService.make_request()`.

## Keeping the fake up to date

This approach is good, but there is one problem: if the public interface of our
real service object changes, we also have to update the fake. This may be
acceptable; after all, it would likely cause a runtime test failure if the
public interface of the fake differed from the real service object. But there is
an easy way to make the compiler do more work for us.

Using [behaviors] we can specify a public interface that both the real service
object and the fake must conform to. This can give us more confidence that we're
always keeping the two in sync with each other.

Let's define a module describing the behavior of our service object:

```elixir
defmodule App.Services.WebServiceProvider do
  @callback make_request() :: HTTPoison.Response.t()
end
```

And then we can adopt that behavior in our service object and fake:

```elixir
defmodule App.Services.WebService do
  alias App.Services.WebServiceProvider
  @behavior WebServiceProvider

  @impl WebServiceProvider
  def make_request do
    HTTPoison.get!("http://thoughtbot.com/")
  end
end

. . .

defmodule App.Services.FakeWebService do
  alias App.Services.WebServiceProvider

  @impl WebServiceProvider
  def make_request do
    %HTTPoison.Response{body: ". . .", status_code: 200}
  end
end
```

## Final thoughts

This approach is great when we want pure unit testing, and our goal is to avoid
any external requests. The pact library even allows us to replace dependencies
in a block, rather than permanently:

```elixir
App.Pact.replace :web_service, FakeWebService do
  . . .
end
```

This can be an invaluable alternative to mocking a dependency for all tests, and
may be preferable if we want to be very explicit about what is mocked in each
test while still allowing us to easily make use of our fake.

For integration tests or more complex services where we want to test the full
service-to-service interaction, we may want to consider
[building our own mock server] instead of replacing the external service with a fake.

[constable]: https://github.com/thoughtbot/constable
[mock]: https://hexdocs.pm/mock/Mock.html
[dependency injection]: https://en.wikipedia.org/wiki/Dependency_injection
[pact]: https://github.com/BlakeWilliams/pact
[behaviors]: https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#behaviours
[building our own mock server]: https://medium.com/flatiron-labs/rolling-your-own-mock-server-for-testing-in-elixir-2cdb5ccdd1a0
