View Source Mockable

Build Status Version Hex Docs Download License Last Updated

Zero boilerplate mock delegation.

Example

use Mockable in a module:

defmodule TemperatureClient do
  use Mockable

  @callback get_temperature(String.t()) :: integer()

  @impl true
  def get_temperature(city) do
    Req.get!("https://weather.com/temperatue/#{city}").body["temperature"]
  end
end

Configure the test environment (config/test.exs) to delegate function calls to the mock:

config :mockable, [
  {TemperatureClient, TemperatureClientMock}
]

Optionally configure the dev environment (config/dev.exs) to delegate function calls to a stub:

config :mockable, [
  {TemperatureClient, TemperatureClientStub},
  log: false
]

The log option shown above controls whether a log is emmitted for each invocation indicating the implementation used. If you are using mockable in prod builds for stubs/fakes in staging environments, you probably want to set log: false for your prod build.

If you don't configure a default implementation for a Mockable module at compile time, all Mockable code for that module will be completely eliminated from the build. It will be as if that module does not use Mockable.

Implement a dev stub like this:

defmodule TemperatureClientStub do
  @behaviour TemperatureClient

  @impl true
  def get_temperature(city) do
    30
  end
end

See docs for more information and examples.

Details

Mockable works by using a __before_compile__ macro to wrap each callback implementation in delegation logic. But it only does this if :mockable is configured, thus it does not affect production code.

Features/Benefits:

  • Zero boilerplate code
  • Can be used with Exunit async: true
  • Compatible with Mox/Hammox (and probably any other mocking library)
  • Applies @callback as @spec on implementations to enable dialyzer checks
  • Configurable with Application environment & process memory
  • Completely compiles out in prod builds, not requiring even an Application.get_env, making it suitable for frequently called functions
  • Behaviour and implementation defined in the same module for easy finding/reading
  • Only overrides callbacks, other functions defined within the Mockable module are not delegated and can be called as normal