Copyright © 2011 - 2018 Sven Heyll
Version: 7.2.2
Authors: Sven Heyll (sven.heyll@gmail.com).
ErlyMock was built to be helpful for Test Driven Development. I know this goal is by no means yet reached, so I ask you kindly to support this project. All suggestions, complaints and improvements are welcome.
ErlyMock is the latest incarnation of an Erlang mocking library inspired by Easymock for Java. It is used in unit tests to verify that a set of functions is called by the code under test in correct order and with correct parameters.
With ErlyMock it is possible to declare the behavior of arbitrary modules/functions with or without expecting acutal invocations. The user simply declares what each function call to some function should return or do at the beginning of a unit test.
Assume there are two modules, used by the code to be tested, that shall be mocked (at least to prevent damaging side effects):
A rocket launcher server:-module(rocket_launcher). launch(Longitude, Latitude, Type) -> ....And A UI module:
-module(rocket_launcher_ui). ask_for_instructions() -> ...Then this is how a happy case unit test might look like:
launche_missle_test() -> % create a new mock process M = em:new(), % define the expectations em:strict(M, rocket_launcher_ui, ask_for_instructions, [], {return, [{longitude, 123}, {latitude, 999}, {type, some_rocket_type}]}), em:strict(M, missle_lauchner, launch, [123, 999, some_rocket_type]), % tell the mock that all expectations are defined em:replay(M), % run code under test rocket_app:interactive(), % verify expectations immediately em:verify(M).
Another example shall show the usefulness of em:await_expectations/1
instead of em:verify/1
to wait for asynchronous invokations and em:zelf/1
as argument matcher matching the process id of the process calling
the mocked functions in the replay phase.
%% Let's look at the impl first: -module(xxx_impl). download_image(Url) -> gen_server:cast(?MODULE, {download, URL}). %% ... handle_cast({download, URL}, State) -> net_io:schedule_download(Url, self()), {noreply, State}. %%%%%%%%%% Now this 'cast' happens asynchronously using 'em:verify/1' would not %%%%%%%%%% help a lot, it would be pure luck if the erlang scheduler executed %%%%%%%%%% that handle_cast before. This is the case where %%%%%%%%%% em:await_expectations/1 comes in handy... %% Let's look at the test first: downld_img_test() -> % create a new mock process M = em:new(), Url = test_url, em:strict(M, net_io, schedule_download, [Url, %% em:zelf() will match 'self()' of the process under test! em:zelf()]), em:replay(M), %% call code under test xxx_impl:download_image(Url), %% await the cast to happen: em:await_expectations(M).
Erlymock provides support for blocking the test process until a specific
expection defined by strict/4
or strict/5
was recorded by the
mock process.
Both strict/4
and strict/5
return a reference to the
expectation they define. The function em:await/2
accepts such a
reference and blocks until the expected invokation happens.
A useful property of em:await/2
is that it returns the pid and the
arguments of the recorded function call. This means that em:await/2
can
also be used to capture parameters and process ids, even when the
synchronization aspect is irrelevant.
COME CODE:
Impl-module(xxx). ... op(Pid) -> gen_server:cast(Pid, op). ... handle_cast(op, State) -> collab:do_it(), {noreply, State}.Test
op_twice_test() -> M = em:new(), DoneIt = em:strict(M, collab, do_it, []), em:replay(M), {ok, Pid} = xxx:start(), xxx:op(Pid), em:await(M, DoneIt), exit(Pid, kill), em:verify(M).
For more details please read the em
module documentation.
ErlyMock has undergone many stages and years of development and usage before reaching this stage.
It was first published here: http://sheyll.blogspot.com/2009/02/erlang-mock-erlymock.html Then Samual Rivas cleaned it up and added support for restoring cover compiled modules for his project, see http://www.lambdastream.com/.
The code was then added with all modifications to the great new erlang-maven-plugin forge which can be found here: http://sourceforge.net/projects/erlang-plugin/.
Then I decided to partially rewrite ErlyMock in order to provide a simpler API and in order to improve the code quality. Also, I wanted to use the gen_fsm OTP standard behavior.
Later loxybjorn on github added rebar support, now erlymock could automatically be added to rebar projects.
Several improvements were added by Olle Törnström, who helped fixing module purging issues.
Finally I decided to remove maven support, and to rely totally on rebar.
Thanks to all who helped with erlymock.Generated by EDoc