EUnit – Common special errors


Now and then, people ask me EUnit questions, and sometimes the problems consist of less obvious nuts and bolts.  So, let’s explore some special and yet common errors.

For the purpose of this post, I will be using my previously seen numberserver (reposted here for your convenience).

%%% @author Gianfranco <zenon@zen.home>
%%% @copyright (C) 2010, Gianfranco
%%% Created :  4 Oct 2010 by Gianfranco <zenon@zen.home>
-module(numberserver).
-export([start/0,stop/0,op/2,get/0,init/0]).
start() ->
    Pid = spawn_link(?MODULE,init,[]),
    register(?MODULE,Pid),
    ok.
stop() -> ?MODULE ! stop, unregister(?MODULE).
op(Op,Num) -> ?MODULE ! {Op,Num}, ok.
get() ->
    ?MODULE ! {get_result,self()},
    receive
        X -> X
    end.
init() -> loop(basic).
loop(E) ->
    receive
        stop -> ok;
        {get_result,From} ->
            From ! E, loop(E);
        {Op,Num} -> loop(result(Op,E,Num))
    end.
result(_,basic,X) -> X;
result('+',X,Y) -> X + Y;
result('*',X,Y) -> X * Y;
result('-',X,Y) -> X - Y;
result('/',X,Y) -> X / Y.

Enough with the reminiscence.

*** test module not found ***   ::ok

This one is very special, and also very common. People sometimes forget that Instantiators (remember, instantiators are functions that take the setup/0 result as input argument, and returns a TestSet or Simple Test Object) must return TestSet or Simple Test Object. Classic erroneous code follows.

example_a_test_() ->
    {setup,
     fun()  -> numberserver:start() end,
     fun(_) -> numberserver:stop() end,
     % Using instantiator, not returning: Test xor Simple Test Object!
     fun(_) ->
         numberserver:op('+',1),
         ?assertEqual(1,numberserver:get())
     end
    }.

and the reduced error output for this

1> ======================== EUnit ========================
module 'numberserver'
  module 'numberserver_tests'
undefined
*** test module not found ***
::ok

=======================================================
  Failed: 0.  Skipped: 0.  Passed: 0.
One or more tests were cancelled.

This error often (most understandably!) confuses people as it does not say: bad test descriptor or something more helpful. The correct code could have been written as

example_a_test_() ->
    {setup,
     fun()  -> numberserver:start() end,
     fun(_) -> numberserver:stop() end,
     fun(_) ->
        fun() ->
             numberserver:op('+',1),
             ?assertEqual(1,numberserver:get())
        end
     end
     }.

Test passed.                (yet it's wrong!)

Sometimes people use {setup,....}, {foreach,...} or any other fixture inside a _test() [simple test function] instead of a _test_() [test-generating function]. EUnit will then seem to execute the test, but does not do it. Proof follows below, with a faulty test-case and the output.

-module(numberserver_tests).
-include_lib("eunit/include/eunit.hrl").

example_b_test() ->
    {setup,
     fun()  -> numberserver:start() end,
     fun(_) -> numberserver:stop() end,
     fun() ->
             numberserver:op('+',3),
             numberserver:op('*',4),
             ?assertMatch(13,numberserver:get())
     end}.

And the console, deceives us with

zen:EUnitProblems zenon$ erlc -o ebin/ src/*.erl test/*.erl
zen:EUnitProblems zenon$ erl -pa ebin/ -eval 'eunit:test(numberserver,[verbose]).'
Erlang R13B04 (erts-5.7.5) [source] [64-bit] [smp:4:4] [rq:4]
[async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.5  (abort with ^G)
1> ======================== EUnit ========================
module 'numberserver'
  numberserver_tests: example_b_test (module 'numberserver_tests')...ok
  [done in 0.002 s]
=======================================================
  Test passed.

1>

This shows us how IMPORTANT, it is to ALWAYS have a failing test first.  If we would have a failing test first, because it's obvious that it's failing, this type of errors can not sneak onto us.

Correct code would have been

-module(numberserver_tests).
-include_lib("eunit/include/eunit.hrl").

example_b_test_() ->
    {setup,
     fun()  -> numberserver:start() end,
     fun(_) -> numberserver:stop() end,
     fun() ->
             numberserver:op('+',3),
             numberserver:op('*',4),
             ?assertMatch(13,numberserver:get())
     end}.

and console output

zen:EUnitProblems zenon$ erlc -o ebin/ src/*.erl test/*.erl
zen:EUnitProblems zenon$ erl -pa ebin/ -eval 'eunit:test(numberserver,[verbose]).'
Erlang R13B04 (erts-5.7.5) [source] [64-bit] [smp:4:4] [rq:4]
[async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.5  (abort with ^G)
1> ======================== EUnit ========================
module 'numberserver'
  module 'numberserver_tests'
    numberserver_tests: example_b_test_...*failed*
::error:{assertMatch_failed,[{module,numberserver_tests},
                           {line,11},
                           {expression,"numberserver : get ( )"},
                           {expected,"13"},
                           {value,12}]}
  in function numberserver_tests:'-example_b_test_/0-fun-2-'/0

    [done in 0.003 s]
  [done in 0.003 s]
=======================================================
  Failed: 1.  Skipped: 0.  Passed: 0.

This post will be updated as time goes and people ask questions, etc. So check it out once in a while!

 

Cheers

/G

About these ads
Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: