Top Banner
Erlang Solutions Ltd. © 2011 Erlang Solutions Ltd. Meck A Mocking Library for Erlang
38

Meck at erlang factory, london 2011

May 24, 2015

Download

Technology

Adam Lindberg

Meck is a fairly new mocking library for Erlang that enables you to easily mock Erlang modules during testing. You can create modules, add functions, specify return values and throw exceptions. You can also look at a history of the calls made to the module, and make assertions based on that history. Meck can be used for any kind of testing, but is particularly useful in unit or component testing where it is often difficult to handle dependencies on external components or libraries. Meck has been used in production in many different projects for about a year.

This talk gives you an overview of the Meck API and looks at how meck is used and works.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Meck at erlang factory, london 2011

Erlang Solutions Ltd.

© 2011 Erlang Solutions Ltd.

MeckA Mocking Library for Erlang

Page 2: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

What Is Mocking?

adjective /mäk/ 1. Not authentic or real, but

without the intention to deceive

Page 3: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

What Is Mocking?

• Dummy objects

• Fake objects

• Stubs

• Mocks

http://martinfowler.com/articles/mocksArentStubs.html

Page 4: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

What Is Mocking?

• Dummy objects

• Fake objects

• Stubs

• Mocksmeck}

http://martinfowler.com/articles/mocksArentStubs.html

Page 5: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Alternative Ways To Mocktest() -> % Setup mock F = "test/mock.erl", {ok, M, B} = compile:file(F, [binary]), code:load_binary(M, F, B),

% Run tests ?assert(system_under_test:run()),

code:purge(mock).

Page 6: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Other Mocking Libraries

• erlymock - https://github.com/sheyll/erlymock

Unmaintained?

• emock - https://github.com/noss/emock

• effigy - https://github.com/cliffmoon/effigy

Page 7: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Create a Module1> meck:new(test).ok

2> test:module_info().[{exports,[{module_info,0}, {module_info,1}]}, {imports,[]}, {attributes,[{vsn,[276740...]}]}, {compile,[{options,[]}, {version,"4.7.4"}, {time,{2011,5,30,11,48,5}}, {source,"."}]}]

Page 8: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Add Functions3> meck:expect(test, foo, fun() -> bar end).ok

4> test:foo().bar

5> meck:expect(test, baz, 0, qux).ok

6> test:baz().qux

Page 9: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Returning Changing Results7> meck:sequence(test, read, 0, ["foo", "bar", eof]).ok

8> test:read()."foo"9> test:read()."bar"10> test:read().eof11> test:read().eof

NEW

Page 10: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Returning Changing Results12> meck:loop(test, count, 0, [1, 2, 3, 2]).ok

13> [test:count() || _ <- lists:seq(1, 10)].[1,2,3,2,1,2,3,2,1,2]

NEW

Page 11: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Delete Functions14> meck:delete(test, foo, 0).ok

15> test:foo().** exception error: undefined function test:foo/0

16> test:module_info().[{exports,[{read,0},{count,0},{baz,0}, {module_info,0}, {module_info,1}]}, ...]

Page 12: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Delete Functions14> meck:delete(test, foo, 0).ok

15> test:foo().** exception error: undefined function test:foo/0

16> test:module_info().[{exports,[{read,0},{count,0},{baz,0}, {module_info,0}, {module_info,1}]}, ...]

Page 13: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Delete Functions14> meck:delete(test, foo, 0).ok

15> test:foo().** exception error: undefined function test:foo/0

16> test:module_info().[{exports,[{read,0},{count,0},{baz,0}, {module_info,0}, {module_info,1}]}, ...]

Image by megapiksel

Page 14: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Linking17>

=ERROR REPORT== 30-May-2011::15:10:55 ==** Generic server test_meck terminating** Last message in was {'EXIT',<0.63.0>, {undef,[{test,foo,[]},...]}}

** When Server state == {state,test,...}

** Reason for termination ==** {'module could not be loaded', [{test,foo,[]},...]}

Page 15: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Linking18> meck:new(test, [no_link]).ok

19> exit(crash).** exception exit: crash

20> test:module_info().[{exports,[{module_info,0}, {module_info,1}]}, ...]

Page 16: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Validating a Module21> meck:validate(test).true22> Foo = fun(A) when is_integer(A) -> bar end.#Fun<erl_eval.6.80247286>23> meck:expect(test, foo, Foo).ok24> test:foo(1).bar25> meck:validate(test).true

Page 17: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Validating a Module26> test:foo(a).** exception error: no function clause matchingerl_eval:'-inside-an-interpreted-fun-'(a)27> meck:validate(test).false

Page 18: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Exceptions28> meck:validate(test).true29> meck:expect(test, foo, fun() -> exit(crash) end).ok30> test:foo().** exception exit: crash in function meck:exec/4 in call from test:foo/0 called as test:foo()31> meck:validate(test).false

Page 19: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Exceptions32> meck:expect(test, foo, fun() -> meck:exception(exit, crash) end).ok33> test:foo().** exception exit: crash in function meck:exception/2 in call from test:foo/0 called as test:foo()34> meck:validate(test).true

Page 20: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Exceptions35> try test:foo() catch _:_ -> erlang:get_stacktrace() end.[{meck,exception,2}, {meck,exec,4}, {test,foo,[]}, {erl_eval,do_apply,5}, {erl_eval,try_clauses,8}, {shell,exprs,7}, {shell,eval_exprs,7}, {shell,eval_loop,3}]

Page 21: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Original Modules36> l(original).{module,original}37> original:a().a38> original:b().b6> meck:new(original, [no_link]).ok7> original:a().** exception error: undefined function original:a/0

Page 22: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Original Modules10> meck:new(original, [no_link, passthrough]).ok11> meck:expect(original, b, 0, z).ok12> original:a().a13> original:b().z

Page 23: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Original Modules14> original:double(1).215> meck:expect(original, double,15> fun(1) -> foo;15> (A) -> meck:passthrough([A]) 15> end).ok16> original:double(1).foo17> original:double(2).4

Page 24: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

History18> meck:expect(original, a, fun(1) -> foo end).ok19> original:a(2).** exception error: function clause...20> meck:history(original).[{{original,a,[]},a}, % {MFA, R} {{original,b,[]},z}, {{original,c,[1]},foo}, {{original,a,[2]}, % {MFA, C, R, ST} error,function_clause, [{original,a,[2]},...]}]

Page 25: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

History21> meck:called(original, a, [2]).true22> meck:called(original, x, ["nope"]).false

Thanks to Susan Potter!

NEW

Page 26: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Multiple Modules29> Mods = [x, y, z].[x,y,z]30> meck:new(Mods).ok31> meck:expect(Mods, foo, 0, bar).ok32> [M:foo() || M <- Mods].[bar,bar,bar]33> meck:validate(Mods).true34> meck:unload(Mods).ok

Page 27: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Using meck With EUnitsome_test() -> ok = meck:new(t), ?assertEqual(result, m:f()), ?assert(meck:called(t, f, [1, a])), ?assert(meck:validate(t)), ok = meck:unload(t).

Page 28: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Using meck With EUnitsome_test_() -> {foreach, fun setup/0, fun teardown/1, [fun test1/0, fun test2/0, fun test3/0]}.

setup() -> Mods = [x, y, z], meck:new(Mods), meck:expect(x, foo, 0, bar), % ... Mods.

teardown(Mods) -> meck:unload(Mods).

Page 29: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Behind the Scenes

Page 30: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Behind the Scenes

test process

Page 31: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Behind the Scenes

mock processtest process

Page 32: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Behind the Scenes

mock processtest process

mocked module

Page 33: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Behind the Scenes

Page 34: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Behind the Scenes

m:f()! {get, m, f}

! #Fun<...>

Resultexecute

meck:expect(m, f, fun() -> Result end)

Page 35: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Behind the Scenes

m:f()

! {log, ...}Result

execute

meck:expect(m, f, 0, Result)

Page 36: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Future Developments

• Smarter code generation

• Better performance

• More history validations helpers

• Integration with Hamcrest for Erlang

- Specific meck helpers

Page 37: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Where Is meck Used?

• ESL - Many internal projects

• Basho - Riak Enterprise

• Hibari - The Hibari key value store

• Mochi Media - In-game Advertising

• 100+ projects on GitHub

Page 38: Meck at erlang factory, london 2011

© 2011 Erlang Solutions Ltd.

Get It, Use It, Improve It!

github.com/eproxus/meck

@eproxus

[email protected]