XUtils

meck

A mocking library for Erlang.


Use

Meck is best used via [Rebar 3][rebar_3]. Add Meck to the test dependencies in your rebar.config:

{profiles, [{test, [{deps, [meck]}]}]}.

Manual Build

Meck uses [Rebar 3][rebar_3]. To build Meck go to the Meck directory and simply type:

rebar3 compile

In order to run all tests for Meck type the following command from the same directory:

rebar3 eunit

Documentation can be generated through the use of the following command:

rebar3 edoc

Test Output

Normally the test output is hidden, but if EUnit is run directly, two things might seem alarming when running the tests:

  1. Warnings emitted by cover
  2. An exception printed by SASL

Both are expected due to the way Erlang currently prints errors. The important line you should look for is All XX tests passed, if that appears all is correct.

Caveats

Global Namespace

Meck will have trouble mocking certain modules since it works by recompiling and reloading modules in the global Erlang module namespace. Replacing a module affects the whole Erlang VM and any running processes using that module. This means certain modules cannot be mocked or will cause trouble.

In general, if a module is used by running processes or include Native Implemented Functions (NIFs) they will be hard or impossible to mock. You may be lucky and it could work, until it breaks one day.

The following is a non-exhaustive list of modules that can either be problematic to mock or not possible at all:

  • erlang
  • supervisor
  • All gen_ family of modules (gen_server, gen_statem etc.)
  • os
  • crypto
  • compile
  • global
  • timer (possible to mock, but used by some test frameworks, like Elixir’s ExUnit)

Local Functions

A meck expectation set up for a function f does not apply to the module- local invocation of f within the mocked module. Consider the following module:

-module(test).
-export([a/0, b/0, c/0]).

a() ->
  c().

b() ->
  ?MODULE:c().

c() ->
  original.

Note how the module-local call to c/0 in a/0 stays unchanged even though the expectation changes the externally visible behaviour of c/0:

3> meck:new(test, [passthrough]).
ok
4> meck:expect(test,c,0,changed).
ok
5> test:a().
original
6> test:b().
changed
6> test:c().
changed

Common Test

When using meck under Erlang/OTP’s Common Test, one should pay special attention to this bit in the chapter on Writing Tests:

init_per_suite and end_per_suite execute on dedicated Erlang processes, just like the test cases do.

Common Test runs init_per_suite in an isolated process which terminates when done, before the test case runs. A mock that is created there will also terminate and unload itself before the test case runs. This is because it is linked to the process creating it. This can be especially tricky to detect if passthrough is used when creating the mock, since it is hard to know if it is the mock responding to function calls or the original module.

To avoid this, you can pass the no_link flag to meck:new/2 which will unlink the mock from the process that created it. When using no_link you should make sure that meck:unload/1 is called properly (for all test outcomes, or crashes) so that a left-over mock does not interfere with subsequent test cases.

Contribute

Patches are greatly appreciated! For a much nicer history, please [write good commit messages][commit_messages]. Use a branch name prefixed by feature/ (e.g. feature/my_example_branch) for easier integration when developing new features or fixes for meck.

Should you find yourself using Meck and have issues, comments or feedback please [create an issue here on GitHub][issues].

Meck has been greatly improved by many contributors!


Articles

  • coming soon...