Packages

A simple Elixir package to elegantly mock module functions within (asynchronous) ExUnit tests using Erlang's :meck library

Current section

Files

Jump to
mecks_unit lib mecks_unit.ex
Raw

lib/mecks_unit.ex

defmodule MecksUnit do
@moduledoc false
def define_mocks(mocks, test_module, mock_index) do
prefix = [Atom.to_string(test_module), mock_index, "."] |> Enum.join()
Enum.each(mocks, fn {mock_module, block} ->
original_module =
mock_module
|> Atom.to_string()
|> String.replace(prefix, "Elixir.")
|> String.to_atom()
if function_exported?(mock_module, :__info__, 1) do
IO.warn("Already defined mock module for #{original_module}")
else
Code.eval_quoted({:defmodule, [import: Kernel], [mock_module, [do: block]]})
Enum.each(mock_module.__info__(:functions), fn {func, arity} ->
mock_function = to_mock_function(original_module, func, arity)
:meck.expect(original_module, func, mock_function)
end)
end
end)
end
defp to_mock_function(module, func, arity) do
arguments = Macro.generate_arguments(arity, :Elixir)
module =
case module do
[_head | _tail] = module -> module
module -> [module]
end
quote do
fn unquote_splicing(arguments) ->
mock_env = MecksUnit.Server.running(self())
mock_module = Module.concat([mock_env, unquote_splicing(module)])
arguments = [unquote_splicing(arguments)]
passthrough = fn(arguments) ->
:meck.passthrough(arguments)
end
if mock_env && function_exported?(mock_module, unquote(func), unquote(arity)) do
try do
case apply(mock_module, unquote(func), arguments) do
{:passthrough, arguments} -> passthrough.(arguments)
:passthrough -> passthrough.(arguments)
returned -> returned
end
rescue
FunctionClauseError ->
passthrough.(arguments)
end
else
passthrough.(arguments)
end
end
end
|> Code.eval_quoted()
|> elem(0)
end
def called(module, func, arguments) do
module
|> history(func)
|> Enum.any?(fn {_pid, {_module, _func, args}, _returned} ->
if length(arguments) == length(args) do
arguments
|> Enum.with_index()
|> Enum.all?(fn {arg, index} ->
(arg == :_) || (Enum.at(args, index) == arg)
end)
end
end)
end
def history(module, func) do
pid = self()
module
|> :meck.history()
|> Enum.reduce([], fn
{^pid, {^module, ^func, _args}, _returned} = call, calls ->
calls ++ [call]
_, calls ->
calls
end)
end
end