tag | 717439512278243ac4cea7ec3235f82a8331f3f0 | |
---|---|---|
tagger | Adam Lindberg <hello@alind.io> | Tue Aug 29 14:10:11 2017 +0200 |
object | 1dd41ae9e6364c802cbc0b7fd75f27492ae76d80 |
Version 0.8.8
commit | 1dd41ae9e6364c802cbc0b7fd75f27492ae76d80 | [log] [tgz] |
---|---|---|
author | Adam Lindberg <hello@alind.io> | Tue Aug 29 14:10:10 2017 +0200 |
committer | Adam Lindberg <hello@alind.io> | Tue Aug 29 14:10:10 2017 +0200 |
tree | 975fa327345e2764940d9873fc85c9f6fb6201c9 | |
parent | fcc551e3feaadf0e1f06451adcd7a2928d7bd66d [diff] |
Version 0.8.8
A mocking library for Erlang.
See what's new in 0.8 Release Notes.
non_strict
)no_link
)unstick
)Here's an example of using Meck in the Erlang shell:
Eshell V5.8.4 (abort with ^G) 1> meck:new(dog, [non_strict]). % non_strict is used to create modules that don't exist ok 2> meck:expect(dog, bark, fun() -> "Woof!" end). ok 3> dog:bark(). "Woof!" 4> meck:validate(dog). true 5> meck:unload(dog). ok 6> dog:bark(). ** exception error: undefined function dog:bark/0
Exceptions can be anticipated by Meck (resulting in validation still passing). This is intended to be used to test code that can and should handle certain exceptions indeed does take care of them:
5> meck:expect(dog, meow, fun() -> meck:exception(error, not_a_cat) end). ok 6> catch dog:meow(). {'EXIT',{not_a_cat,[{meck,exception,2}, {meck,exec,4}, {dog,meow,[]}, {erl_eval,do_apply,5}, {erl_eval,expr,5}, {shell,exprs,6}, {shell,eval_exprs,6}, {shell,eval_loop,3}]}} 7> meck:validate(dog). true
Normal Erlang exceptions result in a failed validation. The following example is just to demonstrate the behavior, in real test code the exception would normally come from the code under test (which should, if not expected, invalidate the mocked module):
8> meck:expect(dog, jump, fun(Height) when Height > 3 -> erlang:error(too_high); (Height) -> ok end). ok 9> dog:jump(2). ok 10> catch dog:jump(5). {'EXIT',{too_high,[{meck,exec,4}, {dog,jump,[5]}, {erl_eval,do_apply,5}, {erl_eval,expr,5}, {shell,exprs,6}, {shell,eval_exprs,6}, {shell,eval_loop,3}]}} 11> meck:validate(dog). false
Here's an example of using Meck inside an EUnit test case:
my_test() -> meck:new(my_library_module), meck:expect(my_library_module, fib, fun(8) -> 21 end), ?assertEqual(21, code_under_test:run(fib, 8)), % Uses my_library_module ?assert(meck:validate(my_library_module)), meck:unload(my_library_module).
Pass-through is used when the original functionality of a module should be kept. When the option passthrough
is used when calling new/2
all functions in the original module will be kept in the mock. These can later be overridden by calling expect/3
or expect/4
.
Eshell V5.8.4 (abort with ^G) 1> meck:new(string, [unstick, passthrough]). ok 2> string:strip(" test "). "test"
It's also possible to pass calls to the original function allowing us to override only a certain behavior of a function (this usage is compatible with the passthrough
option). passthrough/1
will always call the original function with the same name as the expect is defined in):
Eshell V5.8.4 (abort with ^G) 1> meck:new(string, [unstick]). ok 2> meck:expect(string, strip, fun(String) -> meck:passthrough([String]) end). ok 3> string:strip(" test "). "test" 4> meck:unload(string). ok 5> string:strip(" test "). "test"
Meck is best used via Rebar 3. Add the following dependency to your rebar.config
in your project root:
{deps, [ {meck, "0.8.4"} ]}.
Meck requires make
and Rebar 2 to build (included in the repository, to be upgraded to Rebar 3). To build Meck go to the Meck directory and simply type:
make
In order to run all tests for Meck type the following command from the same directory:
make test
Two things might seem alarming when running the tests:
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.
Documentation can be generated through the use of the following command:
make doc
Meck will have trouble mocking certain modules since Meck works by recompiling and reloading modules. Since Erlang have a flat module namespace, replacing a module has to be done globally in the Erlang VM. This means certain modules cannot be mocked. The following is a non-exhaustive list of modules that can either be problematic to mock or not possible at all:
erlang
os
crypto
compile
global
timer
(possible to mock, but used by some test frameworks, like Elixir's ExUnit)Also, 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
Patches are greatly appreciated! For a much nicer history, please write good 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.
Meck has been greatly improved by [many contributors] (https://github.com/eproxus/meck/graphs/contributors)!
If you or your company use Meck and find it useful, Bitcoin donations are greatly appreciated!