How to mock HTTPoison in Elixir?

Using MOX library to simulate HTTPoison behavior.

We, as Elixir developers, extensively use httpoison library which provides an HTTP client for Elixir. Even if a bunch of us are hipsters, and use some less known (but not necessarily worse!) libraries like e.g.:

we can still have HTTPoison somewhere in our dependencies. We use it to call external APIs and make requests to 3rd-party services.

Testing

It’s pretty obvious that, more often than not, we want to avoid outgoing HTTP connections in our tests because of different reasons:

they make them slower

there isn’t always a network connection in test environments

testing credentials to 3rd-party APIs might not be available

quota can be limited or expensive (e.g. Google API)

we don’t want to modify any data in external services

provider may charge us for incoming calls (e.g. sending an SMS)

How can we overcome these obstacles? There are a couple of test objects we could introduce:

Dummies — components whose the only responsibility is to expose the same interface as the real module but with no-ops instead of any implementation. Used when we want to skip some side effects while testing some related logic.

— components whose the only responsibility is to expose the same interface as the real module but with no-ops instead of any implementation. Used when we want to skip some side effects while testing some related logic. Fakes — components with working implementation but different than the production one. They usually take some shortcuts and have simplified version of production code, e.g. in-memory storage.

— components with working implementation but different than the production one. They usually take some shortcuts and have simplified version of production code, e.g. in-memory storage. Mocks — components that register calls they receive to verify all expected actions were performed, e.g. sending emails asynchronously.

— components that register calls they receive to verify all expected actions were performed, e.g. sending emails asynchronously. Stubs — components holding predefined data and using it to answer calls. Used when we cannot, or don’t want to, execute real outbound requests, e.g. an HTTP or a DB call.

— components holding predefined data and using it to answer calls. Used when we cannot, or don’t want to, execute real outbound requests, e.g. an HTTP or a DB call. Spies — components wrapping a real module and, by default, routing all function calls to the original ones, also passing through the original results.

The nomenclature is not consistent though, and definitions usually change depending on who is explaining them.

Mocks and explicit contracts

There are principles introduced in the Elixir community and promoted as the (opinionated) approach to testing with ExUnit . José Valim explains that:

The general rules are:

No ad-hoc mocks — you can only create mocks based on the current behaviours without changing the existing interfaces

— you can only create mocks based on the current behaviours without changing the existing interfaces No dynamic generation of modules — mocks are predefined in test config or setup blocks, and not during tests themselves

— mocks are predefined in test config or setup blocks, and not during tests themselves Concurrency support — they should work in asynchronous mode the same way as in the regular tests

— they should work in asynchronous mode the same way as in the regular tests Pattern matching — they have to rely on native mechanizms and function clauses to assert the input instead of any complex expectation rules

They were introduced because our test tools often make it very easy to abuse mocks so the goal was to provide better guidelines on using them.

Abstraction

We rarely use HTTP client directly in our applications. Of course, at some point, we do that but in our project in general we use some interface which hides the implementation details under the hood.

We may implement something like:

TwitterAPI

TwilioGateway

GmailAdapter

AWSClient

DropboxConnector

And these are the modules we commonly want to mock in our tests. We want to avoid external HTTP calls and just return the expected results from them. However, this is not the case here.

As we already agreed, they all rely on the underlying HTTP client, i.e. calling it, parsing responses, rendering results and handling errors. This also has to be tested at some point, because how can we be sure we covered and handled all 3rd-party service responses as well in our HTTP abstraction layer?

HTTPoison

Like we decided, we’re gonna use httpoison library and leverage only post/4 function. The interface is as follows:

I took that interface from the library itself and I know exactly what and how I will be posting. As you may guess, the Content-Type will be application/x-www-form-urlencoded here.

Once we have our behaviour and know the real adapter, let’s configure our application for testing:

and tests themselves:

Implementation

We already know what we want to do, how our interface looks like, what we want to use, so let’s implement that finally. We will need an HTTP wrapper which will call our HTTP client, handle and parse a corresponding response.

It may look as follows:

What do we do in the Wrapper above?

We fetch http_adapter from our specific config We make an HTTP POST request We parse the received response based on its status code or type

You can imagine how much we would miss if we mocked and tested solely the Http.Wrapper interface!

How to test such implementation now? You may see an example here:

I tried to cover all the usecases I may encounter while calling HTTPoison client. This makes me sure my Wrapper will work as intended.

Subscribe to get the latest content immediately

https://tinyletter.com/KamilLelonek

Summary