Packages
ExDoubles is an opinionated mocking library for Elixir. It takes the stance that the easiest way to create loose coupling in your codebase is to follow the Dependency Inversion Principle (DIP). This framework allows adhoc mocks so that you can emulate edge cases in your tests.
Current section
Files
Jump to
Current section
Files
lib/ex_doubles.ex
defmodule ExDoubles do
alias ExDoubles.{ErrorMessages, State, ListenerFactory}
@spec mock(atom, integer) :: {:ok, function}
def mock(name, arity, stub_value \\ nil)
@spec mock(atom, integer, any) :: {:ok, function}
def mock(name, arity, stub_value) do
listener_fn = ListenerFactory.make_listener(arity, fn args ->
State.increment(name, args)
stub_value
end)
:ok = State.add_mock(%{name: name, arity: arity})
{:ok, listener_fn}
end
@type call_count_matcher :: %{times: integer}
@type argument_matcher :: %{called_with: list(any())}
@spec verify(atom(), call_count_matcher) :: bool
def verify(name, %{called_with: args}) do
%{arity: arity, calls: calls} = State.get_mock(name)
case arity do
0 -> raise ErrorMessages.unsupported_call()
_ ->
case matches?(calls, args) do
true ->
true
false ->
raise ErrorMessages.not_called_error(name, args, calls)
end
end
end
@spec verify(atom(), argument_matcher) :: bool
def verify(name, %{times: n}) do
call_count = State.call_count(name)
case call_count == n do
true ->
true
false ->
State.stop()
raise ErrorMessages.call_count_incorrect(n, call_count)
end
end
defp matches?(calls, args) do
calls
|> Enum.find_index(fn c -> c == args end)
|> is_integer()
end
@spec once :: call_count_matcher
def once() do
%{times: 1}
end
@spec twice :: call_count_matcher
def twice() do
%{times: 2}
end
@spec thrice :: call_count_matcher
def thrice() do
%{times: 3}
end
@spec times(integer) :: call_count_matcher
def times(n) do
%{times: n}
end
@spec called_with(list(any)) :: argument_matcher
def called_with(args) do
%{called_with: args}
end
end