MockBehaviour v0.1.4 MockBehaviour View Source
MockBehaviour helps you maintain mocks for your behaviours.
This code is inspired by the pattern shown in Jose Valim’s article on mocks ( http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ ).
Assume the following behaviour:
defmodule TwitterContract do
@callback tweets(user_id :: String.t) :: [map()]
end
defmodule TwitterContractHttp do
@behaviour TwitterContract
def tweets(user_id), do: # call to Twitter, return desired tweets
end
At this point, your code might be using TwitterContractHttp
and you are now inextricably bound to an external service.
Not so! You can create an API to get to your external services, and use mocks at that level. All you have to do is this:
defmodule Contracts do
@behaviour TwitterContract
@twitter Application.get_env(:app, :contracts)[:twitter_module]
def tweets(user_id), do: @twitter.tweets(user_id)
end
And you can configure your system like so:
# dev
config :app, :contracts, [
twitter_module: TwitterContractHttp
]
# test
config :app, :contracts, [
twitter_module: TwitterContractMock
]
At this point, to reap the rewards from this package, all you have to do is:
defmodule TwitterContractMock do
use MockBehaviour, behaviour: TwitterContract
end
This will generate the following code:
defmodule TwitterContractMock do
use GenServer
def start_link(state) do
GenServer.start_link(__MODULE__, state, name: __MODULE__)
end
def tweets(user_id) do
GenServer.call(__MODULE__, {:tweets, user_id})
end
def handle_call({:tweets, user_id}, _from, state) do
response = state.tweets.(user_id)
{:reply, response, state}
end
end
From there, in your tests you can simply define and use an anonymous function:
tweets = fn(x) -> [%{tweet: "This package is such a timesaver", user: "Trevoke" }]
TwitterContractMock.start_link(%{tweets: tweets})
Enjoy your self-maintaining mocks!