Interfacing to External Dependencies with Behaviours

Testing Elixir — by Andrea Leopardi, Jeffrey Matthias (24 / 80)

The Pragmatic Programmers
The Pragmatic Programmers

--

👈 Dependency Doubles | TOC | Test Doubles: Stubs, Mocks, and Fakes 👉

The interface that the SoggyWaffle.WeatherAPI module provides is simple, as it’s made of just one function. However, if we expanded the functionalities of the SoggyWaffle.WeatherAPI module, it would be hard to keep SoggyWaffle.FakeWeatherAPI up to datedate to mirror SoggyWaffle.WeatherAPI. This situation is a great use case for behaviours, Elixir modules that define a set of functions (an interface) that other modules can agree to implement. We can define a behaviour that specifies how we want to interface with the weather API and then implement that behaviour both in the real weather API interface as well as the fake one.

integration_tests/soggy_waffle/weather_api_behaviour.ex

​ ​defmodule​ SoggyWaffle.WeatherAPI.Behaviour ​do​
​ @callback get_forecast(city :: String.t()) ::
​ {​:ok​, term()} | {​:error​, term()}
​ ​end​

Now we can add the @behaviour SoggyWaffle.WeatherAPI.Behaviour line to both our SoggyWaffle.WeatherAPI module as well as our SoggyWaffle.FakeWeatherAPI module. Having a behaviour for our interface has two benefits. The first is that the behaviour will be checked at compile time; so if we add a function to SoggyWaffle.WeatherAPI.Behaviour but forget to add it to all the modules that implement that behaviour, then…

--

--

The Pragmatic Programmers
The Pragmatic Programmers

We create timely, practical books and learning resources on classic and cutting-edge topics to help you practice your craft and accelerate your career.