Skip to main content

Erlang mock - erlymock

NOTE THIS POST IS OUTDATED!


The project has evolved and can be found here: ErlyMock


Some features



  • Easy to use

  • Design based on easymock

  • Works together with otp: can be used even if the clut is called from another process, by invoking mock:verify_after_last_call(Mock,optional: timeout)

  • custom return functions

  • predefined return functions for returning values, receiving message, throwing exceptions, etc..

  • erlymock automatically purges all modules that were mocked, after verify()

  • Custom argument matchers:

    %% Orderchecking types: in_order, out_of_order, stub;
    %% Answering: {return, ...}|{error, ...}|{throw, ...}|{exit, ...}|{rec_msg, Pid}|{function, Fun(Args) -> RetVal}
    expect(Mock, Type, Module, Function, Arguments, Answer = {AT, _}) when AT==return;AT==error;AT==throw;AT==exit;AT==rec_msg;AT==function ->
    call(Mock, {expect, Type, Module, Function, length(Arguments), {Arguments, Answer}}).

    %% this version of expect is suited for useing custom argument matchers
    expect(Mock, Type, Module, Fun, Arity, MatcherFun, Function) ->
    call(Mock, {expect, Type, Module, Fun, Arity, {custom, MatcherFun, Function}}).

  • multiple module per mock




basic usage



in a typicall unit test you will mock certain dependencies. In order to be able to mock function call in the erlang environment you need hot code swapping to
replace the actual implementations with mock functions that look exactly like their orignals to the outside, but that talk to a mock control process,
that you control in order to assert, that functions are called in the correct order and with corrent parameters.
Mocking a process is easier as you can always create a mock process that you smuggle into the code under test.
With erlymock however, you can do both at ease.

The general skeleton of unit test looks like this:

xxx_test() ->
M = mock:new(),
...
mock:strict(M, ModulToMock, FunctionToMock, ArgumentLiteralList, AnswerDefinition)
...
mock:replay(M)
...
do the actual testing
...
mock:verify(M)


Please refer to the easymock for java documentation to understand the basic principles behind this.
Restrictions

  1. Do not mock system modules.

  2. at the moment only whole modules can be replaced, it is not possible(nor desirable in most cases) to mix original module code and generated mock code



API



There are only eight public function calls, of wich only 5 are neede for most tasks. They are:

new() -> Mock

use this to create a new instance of a mock process that is in programming phase

expect(Mock, OrderCheckingType, Module, Function, Arguments, Answer)

expect has the following options:
Orderchecking types:

  1. in_order all calles flaged in_order must be called in the order recorded, an out of order or stub call will not effect the order of stub call. in_order calls have the highest priorite, they are always check before out of order calls or stub calls. That allows for intermixing of calls to the same functions from all three classes. However if possible in_order calls will be matched before oo and stub calls. All in_order call must occur in the correct order.
  2. out_of_order the main diffrences to in_order calls is that these calls may come in any order, but they must still all be made
  3. stub stub calls are not checked at all, stub calls are usefull for providing fixtures, think of it as comfort noise for the class under test.

Answering:


The last parameter of the expect function defines the answer. It is tuple where the first element is one of the following described atoms,

  • {return, Value} allows you to just specify a value that will be returned from the function call

  • {error, Reason} specifies that a certain error will be thrown with erlang:error(Reason)

  • {throw, What} will make the function throw an exception when called

  • exit this will cause the function to call exit when invoked

  • {rec_msg,...} this will caus teh function to make a receive, receiving any message and sending it to a pid

  • {function, F} when the function is invoked, a user defined function can be passed as parameter. The function will be called with the arguments of the mock invocation in an array



Convenience functions


A variant of expect is suited for using custom argument matchers:

expect(Mock, Type, Module, Fun, Arity, MatcherFun, Function)


A short cut for
expect(.., in_order, ..)
is:

strict(Mock, M,F,Args, Answer)


A short cut for
expect(.., out_of_order, ..)
is:

o_o(Mock, M,F,Args, Answer)


A short cut for
expect(.., stub, ..)
is:

stub(Mock, M,F,Args, Answer)


After the programming phase call this to switch to the replay phase:

replay(Mock)


After the verification phase use this to verify that all expected invocations occured:

verify(Mock)


Example code



These are the unit tests for erlymock, they have a certain documentory value...

-module(testtest).
-compile(export_all).

suite() ->
test0(),
test0a(),
test0b(),
test0c(),
test0d(),
test0e(),
test1(),
test2(),
test3(),
test3a(),
test3b(),
test4(),
test4a(),
test5(),
test6(),
test6a(),
test7(),
test7a(),
io:format("~n~nfinished without unexpected errors! error reports may be ignored!!~n~n~n").

test0() ->
Mock = mock:new(),
mock:expect(Mock, in_order, testmodule1, mockme1, [1,2], {return, ok}),
mock:expect(Mock, in_order, testmodule1, mockme2, [2,3], {return, ok}),
mock:expect(Mock, in_order, testmodule2, mockme2, [3,2], {return, ok}),
mock:expect(Mock, in_order, testmodule2, mockme1, [1,2], {return, ok}),

mock:replay(Mock),

testmodule1:mockme1(1,2),
testmodule1:mockme2(2,3),
testmodule2:mockme2(3,2),
testmodule2:mockme1(1,2),

mock:verify(Mock).

test0a() ->
Mock = mock:new(),
mock:expect(Mock, in_order, testmodule1, mockme1, [1,2], {return, ok}),
mock:expect(Mock, in_order, testmodule1, mockme2, [2,3], {return, ok}),
mock:expect(Mock, in_order, testmodule2, mockme2, [3,2], {return, ok}),
mock:expect(Mock, in_order, testmodule2, mockme1, [1,2], {return, ok}),

mock:replay(Mock),

testmodule1:mockme1(1,2),
testmodule1:mockme2(2,3),
{'EXIT',_} = (catch testmodule2:mockme1(1,2)).

test0b() ->
Mock = mock:new(),
mock:expect(Mock, in_order, testmodule1, mockme1, [1,2], {return, ok}),
mock:expect(Mock, in_order, testmodule1, mockme2, [2,3], {return, ok}),
mock:expect(Mock, in_order, testmodule2, mockme2, [3,2], {return, ok}),
mock:expect(Mock, in_order, testmodule2, mockme1, [1,2], {return, ok}),

mock:replay(Mock),

testmodule1:mockme1(1,2),
testmodule1:mockme2(2,3),
testmodule2:mockme2(3,2),

{error, _} = mock:verify(Mock).

test0c() ->
Mock = mock:new(),
mock:strict(Mock, testmodule1, mockme1, [1,2], {return, ok}),
mock:o_o (Mock, testmodule1, mockme2, [2,3], {return, ok}),
mock:strict(Mock, testmodule2, mockme2, [3,2], {return, ok}),
mock:o_o (Mock, testmodule2, mockme1, [1,2], {return, ok}),

mock:replay(Mock),

testmodule1:mockme1(1,2),
testmodule2:mockme2(3,2),
testmodule2:mockme1(1,2),
testmodule1:mockme2(2,3),

mock:verify(Mock).

test0d() ->
Mock = mock:new(),
mock:strict(Mock, testmodule1, mockme1, [1,2], {return, ok}),
mock:o_o (Mock, testmodule1, mockme2, [2,3], {return, ok}),
mock:strict(Mock, testmodule2, mockme2, [3,2], {return, ok}),
mock:o_o (Mock, testmodule2, mockme1, [1,2], {return, ok}),

mock:replay(Mock),

testmodule1:mockme1(1,2),
testmodule2:mockme2(3,2),
testmodule2:mockme1(1,2),

{error, _} = mock:verify(Mock).

test0e() ->
Mock = mock:new(),
mock:strict(Mock, testmodule1, mockme1, [1,2], {return, ok}),
mock:o_o (Mock, testmodule1, mockme2, [2,3], {return, ok}),
mock:strict(Mock, testmodule2, mockme2, [3,2], {return, ok}),
mock:o_o (Mock, testmodule2, mockme1, [1,2], {return, ok}),

mock:stub (Mock, testmodule3, mockme, [666], {return, ok}),
mock:stub (Mock, testmodule3, mockme, [777], {return, ok}),
mock:stub (Mock, testmodule3, mockme, [888], {return, ok}),
mock:stub (Mock, testmodule3, mockme, [999], {return, ok}),

mock:replay(Mock),

ok = testmodule1:mockme1(1,2),
ok = testmodule2:mockme2(3,2),
ok = testmodule3:mockme(777),
ok = testmodule2:mockme1(1,2),
ok = testmodule3:mockme(777),
ok = testmodule3:mockme(888),
ok = testmodule1:mockme2(2,3),
ok = testmodule3:mockme(777),

mock:verify(Mock).

test1() ->
Mock = mock:new(),
mock:replay(Mock),
mock:verify(Mock).

%error expected
test2() ->
Mock = mock:new(),
mock:strict(Mock, testmodule, mockme, [1,2],{return, ok}),
mock:replay(Mock),
{error, _} = mock:verify(Mock).


test3() ->
Mock = mock:new(),
mock:strict(Mock, testmodule2, mockme1, [666], {error, end_of_times}),
mock:replay(Mock),
try testmodule2:mockme1(666) of
_ ->
exit(error_expected)
catch
error:end_of_times ->
ok
end.

test3a() ->
Mock = mock:new(),
mock:o_o(Mock, testmodule2, mockme1, [666], {throw, end_of_times}),
mock:replay(Mock),
try testmodule2:mockme1(666) of
_ ->
exit(error_expected)
catch
throw:end_of_times ->
ok
end.

test3b() ->
Mock = mock:new(),
mock:strict(Mock, testmodule2, mockme1, [666], {exit, end_of_times}),
mock:replay(Mock),
try testmodule2:mockme1(666) of
_ ->
exit(error_expected)
catch
exit:end_of_times ->
ok
end.

test4() ->
Mock = mock:new(),
{error,_} = mock:verify(Mock).

test4a() ->
Mock = mock:new(),
{'EXIT',_} = (catch mock:strict(Mock, testmodule1, mockme1, [1,2], {hier_steht_was_falsches, xxx})).

test5() ->
Mock = mock:new(),
mock:strict(Mock, testmodule1, mockme1, [1,2], {rec_msg, self()}),
mock:replay(Mock),
TestPid = spawn(testmodule1,mockme1,[1,2]),
TestPid ! test,
receive
test ->
ok,
mock:verify(Mock)
after 1000 ->
error
end.

test6() ->
Mock = mock:new(),
mock:o_o(Mock, testmodule1, mockme1, [1,2], {function, fun(X,Y) ->
X + Y
end}),
mock:replay(Mock),
R = testmodule1:mockme1(1,2),
mock:verify(Mock),
3 = R.

test6a() ->
Mock = mock:new(),
mock:stub(Mock, testmodule1, mockme1, [1,2], {function, fun(_,_) ->
erlang:error(test)
end}),
mock:replay(Mock),
{'EXIT',_} = (catch testmodule1:mockme1(1,2)),
mock:verify(Mock).

test7() ->
Mock = mock:new(),
mock:expect(Mock, in_order, testmodule1, mockme1, 1,
fun([{qXYZ, D, B, A}]) when A >= B andalso B >= D ->
true
end,
{function, fun({qXYZ, D, B, A}) ->
[B,D|A]
end}
),
mock:replay(Mock),
L = testmodule1:mockme1({qXYZ, 1,2,3}),
mock:verify(Mock),
[2,1|3] = L.

test7a() ->
Mock = mock:new(),
mock:expect(Mock, in_order, testmodule1, mockme1, 1,
%% hier fehlen die obligatorischen Klammern
fun({qXYZ, D, B, A}) when A >= B andalso B >= D ->
true
end,
{function, fun({qXYZ, D, B, A}) ->
[B,D|A]
end}
),
mock:replay(Mock),
{'EXIT',_}= (catch testmodule1:mockme1({qXYZ, 1,2,3})).



Most important: erlymock source code

Contents of file mock.erl:

-module(mock).
-export([await/1,await/2,signal_fun/2,new/0, expect/7, expect/6, strict/6, strict/5, o_o/5, stub/5, replay/1, verify/1, verify_after_last_call/1, verify_after_last_call/2, invocation_event/1]).

-author('Sven Heyll').
-version(2).

%% use this to create a new instance of a mock process that is in programming phase
new() ->
Mock = spawn_link(fun() -> program_mock([],[],[]) end),
error_logger:info_msg("mock ~w: created~n", [Mock]),
Mock.

%% expect has the following options:
%% Orderchecking types: in_order, out_of_order, stub;
%% Answering: {return, ...}|{error, ...}|{throw, ...}|{exit, ...}|{rec_msg, Pid}|{function, Fun(Args)} -> RetVal}|{function1, Fun(ArgList)}
expect(Mock, Type, Module, Function, Arguments, Answer = {AT, _}) when is_list(Arguments), AT==return;AT==error;AT==throw;AT==exit;AT==rec_msg;AT==function;AT==function1 ->
call(Mock, {expect, Type, Module, Function, length(Arguments), {Arguments, Answer}}).

%% this version of expect is suited for useing custom argument matchers
expect(Mock, Type, Module, Fun, Arity, MatcherFun, Answer) when is_integer(Arity), is_function(MatcherFun)->
call(Mock, {expect, Type, Module, Fun, Arity, {custom, MatcherFun, Answer}}).

%% this is a short cut for expect(.., in_order, ..)
strict(Mock, M,F,Arity, Fun, Answer) when is_integer(Arity)->
expect(Mock, in_order, M, F, Arity, Fun, Answer).

%% this is a short cut for expect(.., in_order, ..)
strict(Mock, M,F,Args, Answer) ->
expect(Mock, in_order, M, F, Args, Answer).

%% this is a short cut for expect(.., in_order, ..)
o_o(Mock, M,F,Args, Answer) ->
expect(Mock, out_of_order, M, F, Args, Answer).

%% this is a short cut for expect(.., in_order, ..)
stub(Mock, M,F,Args, Answer) when is_list(Args)->
expect(Mock, stub, M, F, Args, Answer);

%% this is a short cut for expect(.., in_order, ..)
stub(Mock, M,F,Arity, Answer) when is_integer(Arity) ->
expect(Mock, stub, M, F, Arity, fun(_) -> true end, Answer).

%% after the programming phase call this to switch to the replay phase
replay(Mock) ->
call(Mock, replay).

%% after the verification phase use this to verify that all expected invocations occured
verify(Mock) ->
verify_after_last_call(Mock, 0).

%% after the verification phase use this to verify that all expected invocations occured
verify_after_last_call(Mock) ->
verify_after_last_call(Mock, 1500).
verify_after_last_call(Mock, TimeOut) ->
catch(await(invocation_list_empty, TimeOut)),
call(Mock, verify),
await(cleanup_finished),
error_logger:info_msg("mock ~p: verify finished~n~n~n", [Mock]).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% utility functions for dealing with mock code called from a new process.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% this will create an answering function that captures the current pid and
%% sends an atom to that pid, make sure to use await(atom()) to block until that
%% message is sent
signal_fun(Atom, RetVal) ->
SelfP = self(),
{function1, fun(_) ->
signal(SelfP, Atom),
RetVal
end}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% internal signal function that send a message in "signal" protocol format to
%% some "await(...)"
signal(Pid, Atom) ->
error_logger:info_msg("signalling ~p from ~p to ~p~n", [Atom, self(), Pid]),
Pid ! {mock_signal, Atom}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% this block the current process until signal(SameAtom) from another process is
%% invoked
await(Atom) when is_atom(Atom) ->
await(Atom, 2000).

await(Atom,To) when is_atom(Atom)->
error_logger:info_msg("now awaiting ~p in process ~p~n", [Atom, self()]),
receive
{mock_signal, Atom} ->
error_logger:info_msg("await succeeded: ~p~n", [Atom])
after To ->
fail({timeout, await, Atom})
end.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

fail(Pid, Reason) ->
error_logger:info_msg("mock ~w: failed: ~w~n",[self(), Reason]),
Pid ! {error, Reason}.

fail(Reason) ->
error_logger:info_msg("mock ~w: failed: ~w~n",[self(), Reason]),
throw({mock_failure, Reason}).

success(Pid) ->
Pid ! {response, ok},
success().

success() ->
error_logger:info_msg("mock ~w: successfully finished.~n",[self()]),
test_passed.

call(Name, Request) ->
Name ! {self(), Request},
receive
{error, Reason} ->
throw({mock_failure, Reason});
{response, Response} ->
Response
end.

filter_fun(_, _, _, {Arguments, _}) ->
fun(Args) ->
Args == Arguments
end;

filter_fun(_, _, _, {custom, Matcher, _}) ->
Matcher.

answer_fun({_, Answer}) ->
Answer;

answer_fun({custom, _, Answer}) ->
Answer.

module_header_abstract_form(Mod) ->
[{attribute,0,module,Mod},
{attribute,0,compile,[export_all]}].

fundef_to_abstract_meta_form(Self, Mod, FunName, Arity) ->
Line = 1,
Params = [{var, Line, list_to_atom("A" ++ integer_to_list(I))} || I <- seq(1,Arity)],
{function, Line, FunName, Arity,
[{clause, Line,
Params, [],
[{call, Line,
{remote, Line, {atom, Line, mock}, {atom, Line, invocation_event}},
[{tuple, Line,
[{string,Line, Self}, {atom, Line, Mod}, {atom,Line, FunName}, {integer, Line, Arity},
lists:foldr(
fun(E,R) ->
{cons, Line, E, R}
end,
{nil, Line},
Params)]}]}]}]}.

compile_and_load_abstract_form(AbsForm) ->
CompRes = compile:forms(AbsForm),
{ok, Mod, Code} = CompRes,
code:purge(Mod),
code:delete(Mod),
{module, _} = load_module(Mod, Code).

extract_module_set(Combined) ->
sets:from_list(lists:map(fun([{M,_,_}|_]) -> M end, Combined)).

program_mock(InOrder, OutOfOrder, Stub) ->
receive
{From, {expect, Type, Mod, Fun, Arity, Arg}} ->
FunDef = [{Mod, Fun, Arity} | {filter_fun(Mod,Fun,Arity,Arg), answer_fun(Arg)}],
From ! {response, ok},
case Type of
in_order ->
program_mock([FunDef | InOrder], OutOfOrder, Stub);
out_of_order ->
program_mock(InOrder, [FunDef | OutOfOrder], Stub);
stub ->
program_mock(InOrder, OutOfOrder, [FunDef | Stub])
end;

{From, replay} ->
Self = pid_to_list(self()),
Combined = InOrder ++ OutOfOrder ++ Stub,
ModuleSet = extract_module_set(Combined),
sets:fold( fun (Mod, _) ->
FunsOfModSet = sets:from_list(
lists:foldl(
fun([{M,F,A}|_], Acc) ->
if Mod == M -> [{F,A}|Acc];
true -> Acc
end
end, [], Combined)),
HeaderForm = module_header_abstract_form(Mod),
FunctionForms = sets:fold(
fun({F,A},FFAcc) ->
[fundef_to_abstract_meta_form(Self, Mod, F, A)|FFAcc]
end,
[],
FunsOfModSet),
CLRes = compile_and_load_abstract_form(HeaderForm ++ FunctionForms),
error_logger:info_msg("mock ~w: created and loaded mock code ~w~n",[self(),CLRes])
end,
[],
ModuleSet),
From ! {response, ok},
%% spawn a cleanup process that will call the uninstall fun
auto_cleanup(fun() ->
uninstall(ModuleSet),
signal(From, cleanup_finished)
end),
record_invocations(lists:reverse(InOrder),
OutOfOrder,
Stub,
fun() ->
signal(From, invocation_list_empty)
end
);
{From, What} ->
fail(From, {invalid_state, What})
end.

record_invocations([], [], Stub, EmptyFun) when is_function(EmptyFun) ->
EmptyFun(),
record_invocations([], [], Stub, undefined);
record_invocations(InOrder, OutOfOrder, Stub, EF) ->
%% wait for all incoming invocations, expect every invocation and crash if the invocation was not correct
receive
Invocation = {ProcUnderTestPid, Mod, Fun, Arity, Args} ->
InvMatcher = fun ([{M,F,A}|{Pred,_}]) ->
{M,F,A} == {Mod, Fun, Arity} andalso Pred(Args)
end,
try
case InOrder of
[Test| _] -> InvMatcher(Test);
[] -> false
end
of
true ->
[[_|{_,Function}]| IOR] = InOrder,
ProcUnderTestPid ! {mock_process_gaurd__, Function},
record_invocations(IOR, OutOfOrder, Stub, EF);

false ->
case lists:splitwith(InvMatcher, OutOfOrder) of
{[OOODef|Rest1],Rest2} ->
[_|{_,Function}] = OOODef,
ProcUnderTestPid ! {mock_process_gaurd__, Function},
record_invocations(InOrder, Rest1 ++ Rest2, Stub, EF);

{[], _} ->
case lists:filter(InvMatcher, Stub) of
[StubDef|_] ->
[_|{_,Function}] = StubDef,
ProcUnderTestPid ! {mock_process_gaurd__, Function},
record_invocations(InOrder, OutOfOrder, Stub, EF);

_ ->
EF(),
Reason = {unexpected_invocation, Invocation},
ProcUnderTestPid ! {mock_process_gaurd__, {error, Reason}},
fail(Reason)
end
end
catch
ET:EX ->
Reason = {matching_function_is_incorrent, Invocation, {ET, EX}},
ProcUnderTestPid ! {mock_process_gaurd__, {error, Reason}},
EF(),
fail(Reason)
end;

{From, verify} ->
case {InOrder,OutOfOrder} of
{[],[]} ->
success(From);
MissingRest ->
fail(From,{expected_invocations_missing, MissingRest})
end;

{From, What} ->
EF(),
fail(From, {invalid_state, What})

end.

invocation_event({MockPidStr, Mod, Fun, Arity, Args}) ->
MockPid = list_to_pid(MockPidStr),
error_logger:info_msg("mock ~w: invocation: ~w:~w/~w ~w~n",[MockPid, Mod, Fun, Arity, Args]),
MockPid ! {self(), Mod, Fun, Arity, Args},
receive
{mock_process_gaurd__, {return, Answer}} ->
Answer;
{mock_process_gaurd__, {error, E}} ->
erlang:error(E);
{mock_process_gaurd__, {throw, E}} ->
throw(E);
{mock_process_gaurd__, {exit, R}} ->
exit(R);
{mock_process_gaurd__, {function, F}} ->
error_logger:info_msg("mock ~w: invoking answerer~n",[MockPid]),
R = apply(F,Args),
error_logger:info_msg("mock ~w: answerer returned: ~w~n",[MockPid,R]),
R;
{mock_process_gaurd__, {function1, F}} ->
error_logger:info_msg("mock ~w: invoking answerer~n",[MockPid]),
R = F(Args),
error_logger:info_msg("mock ~w: answerer returned: ~w~n",[MockPid,R]),
R;
{mock_process_gaurd__, {rec_msg, P}} ->
error_logger:info_msg("mock ~w: receiving message for ~w~n",[MockPid,P]),
Msg = receive
M ->
P ! M
end,
error_logger:info_msg("mock ~w: message ~w delivered to ~w~n",[MockPid,Msg,P])
end.

seq(A, E) when A > E -> [];
seq(A, E) -> lists:seq(A,E).

uninstall(ModuleSet) ->
lists:map(fun(Mod) ->
error_logger:info_msg("Deleting and purging module ~p~n", [Mod]),
code:purge(Mod),
code:delete(Mod)
end, sets:to_list(ModuleSet)).

auto_cleanup(CleanupFun) ->
spawn_link(fun() ->
erlang:process_flag(trap_exit, true),
error_logger:info_msg("auto cleanup handler ~p waiting for the end...~n", [self()]),
receive
Msg = {'EXIT', _From, _Reason} ->
error_logger:info_msg("auto cleanup handler ~p receive exit message ~p.~n", [self(), Msg]),
CleanupFun();
_Ather ->
error_logger:info_msg("auto cleanup handler ~p received unexpected message ~p.~n", [self(), _Ather])
end
end).


Comments

Samuel said…
Nice implementation!

I've sort of stolen it for our testing library at LambdaStream.

We are going to do some cosmetic changes and, if needed, some adaptations. You can track them in our github repository.

Of course, you're credited as the original authro, but I added myself so that our developers have someone closer to complain ;)
Sven Heyll said…
Hi Samuel

Thanks for appreciating erlymock,
it is nice to see that it serves some purpose.

Anyway I've stolen the code back and
integrated it back in here:
http://sourceforge.net/projects/erlang-plugin/

You might be interested in building an Erlang project with maven!

Popular posts from this blog

Learning Haskell, functional music

As you might have realized, I started to learn Haskell. One of the most fun things to do in any programming language is creating some kind of audible side effects with a program. Already back in the days when I started programming, I always played around with audio when toying around with a new language. I have found a wonderful set of lecture slides about haskell and multimedia programming, called school of expression. Inspired by the slides about functional music I implemented a little song. Ahh ... and yes it is intended to sound slightly strange . I used the synthesis toolkit to transform the music to real noise, simply by piping skini message to std-out. I used this command line to achieve the results audible in the table: sven@hhi1214a:~/Mukke$ ghc -o test1 test1.hs && ./test1 | stk-demo Plucked -n 16 -or -ip Sound samples: Plucked play Clarinet play Whistle(attention very crazy!) play As always the source... stueck = anfang :+: mitte :+: ende anfang = groovy :+: (Trans ...

The purpose of the MOCK

In response to a much nicer blog entry, that can be found here . There are actually several distinct "tests" that make up usual unit tests, among them two that really do stand out: one kind of testing to test method flows, one to test some sort of computation. Mock objects are for the purpose of testing method flows. A method flow is a series of message transmissions to dependent objects. The control flow logic inside the method(the ifs and whiles) will alter the flow in repsonse to the parameters of the method call parameters passed by calling the method under test, depending on the state of the object that contains the method under test and the return values of the external method calls(aka responses to the messages sent). There should be one test method for every branch of an if statement, and usuale some sort of mock control objects in the mock framework will handle loop checking. BTW: I partly use message transmission instead of method invocation to include other kind...

Keys, Values and Rules: Three Important Shake Concepts

The title was a click-bait! This article will actually try to explain five instead of three important notions in Shake. These are: Rules Keys Values The Build Database Actions This short blog post was inspired by the hurdles with my Shake based build, after the new Shake version was released, which had breaking API changes. Jump to the next section if you are not interested in the why and how of this blog post. Shake is rule based build system much like GNU make. Like make it is robust, unlike make, it is pretty fast and supports dynamic build dependencies. But you knew all that already, if you are the target audience of this post, since this post is about me explaining to myself by explaining to you, how that build tool, I used for years, actually works. Although I used it for years, I never read the paper or wrapped my head around it more than absolutely necessary to get the job done. When Shake was updated to version 0.16.x, the internal API for custom rules w...