A mocking library for Erlang
Latest commit 356cfa7 Apr 30, 2017 @tomas-abrahamsson tomas-abrahamsson committed with Support Erlang 20 AtU8 beam chunk
Tested with commit ffa80a41370025ed2fb95967e731f13cc7e45e4f
and Erlang 19.2.1
Failed to load latest commit information.
doc Switch to rebar3 Sep 27, 2016
include Add ?FORGET_CALLS() which forgets all calls Dec 12, 2013
test Increase timeouts for eunit tests without fixture Mar 1, 2015
.gitignore Ignore rebar's .eunit directory Apr 24, 2013
LICENSE Add license Nov 1, 2011
README.md Switch to rebar3 Sep 27, 2016
rebar.config Switch to rebar3 Sep 27, 2016
rebar.lock Switch to rebar3 Sep 27, 2016



Hex pm

mockgyver is an Erlang tool which will make it easier to write EUnit tests which need to replace or alter (stub/mock) the behaviour of other modules.

mockgyver aims to make that process as easy as possible with a readable and concise syntax.

mockgyver is built around two main constructs: ?WHEN which makes it possible to alter the behaviour of a function and another set of macros (like ?WAS_CALLED) which check that a function was called with a chosen set of arguments.

Read more about constructs and syntax in the documentation for the mockgyver module.

The documentation is generated using the edown extension which generates documentation which is immediately readable on github. Remove the edown lines from rebar.config to generate regular edoc.

A quick tutorial

Let's assume we want to make sure a fictional program sets up an ssh connection correctly (in order to test the part of our program which calls ssh:connect/3) without having to start an ssh server. Then we can use the ?WHEN macro to replace the original ssh module and let connect/3 return a bogus ssh_connection_ref():

    ?WHEN(ssh:connect(_Host, _Port, _Opts) -> {ok, ssh_ref}),

Also, let's mock close/1 while we're at it to make sure it won't crash on the bogus ssh_ref:

    ?WHEN(ssh:close(_ConnRef) -> ok),

When testing our program, we want to make sure it calls the ssh module with the correct arguments so we'll add these lines:

    ?WAS_CALLED(ssh:connect({127,0,0,1}, 2022, [])),

For all of this to work, the test needs to be encapsulated within either the ?MOCK macro or the ?WITH_MOCKED_SETUP (recommended for eunit). Assume the test case above is within a function called sets_up_and_tears_down_ssh_connection:

    sets_up_and_tears_down_ssh_connection_test() ->
        ?MOCK(fun sets_up_and_tears_down_ssh_connection/0).

Or, if you prefer ?WITH_MOCKED_SETUP:

    ssh_test_() ->
        ?WITH_MOCKED_SETUP(fun setup/0, fun cleanup/1).

    sets_up_and_tears_down_ssh_connection_test(_) ->

Sometimes a test requires a process to be started before a test, and stopped after a test. In that case, the latter is better (it'll automatically export and call all ...test/1 functions).

The final test case could look something like this:


    ssh_test_() ->
        ?WITH_MOCKED_SETUP(fun setup/0, fun cleanup/1).

    setup() ->

    cleanup(_) ->

    sets_up_and_tears_down_ssh_connection_test(_) ->
        ?WHEN(ssh:connect(_Host, _Port, _Opts) -> {ok, ssh_ref}),
        ?WHEN(ssh:close(_ConnRef) -> ok),
        ...start the program and trigger the ssh connection to open...
        ?WAS_CALLED(ssh:connect({127,0,0,1}, 2022, [])),
        ...trigger the ssh connection to close...

API documentation

See mockgyver for details.


There are some pitfalls in using mockgyver that you might want to know about.

  • It's not possible to mock local functions.

    This has to do with the way mockgyver works through unloading and loading of modules.

  • Take care when mocking modules which are used by other parts of the system.

    Examples include those in stdlib and kernel. A common pitfall is mocking io. Since mockgyver is potentially unloading and reloading the original module many times during a test suite, processes which are running that module may get killed as part of the code loading mechanism within Erlang. A common situation when mocking io is that eunit will stop printing the progress and you will wonder what has happened.

  • NIFs cannot be mocked and mockgyver will try to inform you if that is the case.

  • mockgyver does currently not play well with cover and cover will complain that a module has not been cover compiled. This is probably solvable.


It all started when a friend of mine (Tomas Abrahamsson) and I (Klas Johansson) wrote a tool we called the stubber. Using it looked something like this:

        [{math, pi, fun() -> 4 end},
         {some_module, some_function, fun(X, Y) -> ... end},
        fun() ->
            code which should be run with replacements above

Time went by and we had test cases which needed a more intricate behaviour, the stubs grew more and more complicated and that's when I thought: it must be possible to come up with something better and that's when I wrote mockgyver.

Using mockgyver with your own application

mockgyver is available as a hex package. Just add it as a dependency to your rebar.config:

{deps, [mockgyver]}.

... or with a specific version:

{deps, [{mockgyver, "some.good.version"}]}.


Build mockgyver using rebar. Also, parse_trans (for the special syntax), edown (for docs on github) and eunit_addons (for ?WITH_MOCKED_SETUP) are required, but rebar takes care of that.

$ git clone git://github.com/klajo/mockgyver.git
$ rebar3 compile

Build docs using the edown library (for markdown format):

$ rebar3 as edown edoc

Build regular docs:

$ rebar3 edoc