View Source Mockable
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