[erlang-questions] eunit, fixtures and processes

Patrick Roemer judgefang@REDACTED
Sun May 11 16:07:49 CEST 2008


Hi,

as an Erlang novice, I'm currently struggling with the correct way to
set up eunit tests involving processes and fixtures.

The basic idea is this: Create the "server process under test" (PUT) on
setup, pass self() (the test module process) as a mock client process,
and register the PUT with a name used for lookup within the test cases.
Stop the PUT on teardown. (Note that it is intended that the client is
carried around as state for the whole life cycle.)

This seems to work nicely, but now I'd like to avoid the registration
and pass the PUT PID around as a fixture instead. But trying this, I
suddenly run into trouble, because the process running setup (and
registering self() as the mock client) is a different one than the
process running the actual test case, expecting to receive messages from
the server.

The eunit documentation suggests "local" mode (instead of the "spawn"
default) to run setup/teardown and tests in the same process. However,
in my setup this only works for the first test case, subsequent ones
still seem to run in dedicated processes.

My best guess that I still haven't groked the basic mechanics and idioms
of fixture setup and instantiators in eunit. I have tried to express
test creation in different ways, but almost always ended up with "bad
test" failures. :( Or is the test design as such inappropriate? I have
attached a minimalist sample below. If somebody please could push me
into the right direction - any help appreciated.

Best regards,
Patrick

Code:
<snip>
-module(fixture_sample).

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

-export([echo/1]).

-define(TIMEOUT, 50).

echo(Client) ->
    receive
        stop -> void;
        Msg ->
            eunit:debug({'echo', Client, Msg}),
            Client ! {echo, Msg},
            echo(Client)
    end.

all_tests_test_() ->
    {foreach, local,
        fun test_setup/0,
        fun test_tear_down/1,
        [
            instantiate_test(fun test_one/1),
            instantiate_test(fun test_two/1)
        ]
    }.

test_setup() ->
    eunit:debug({'setup', self()}),
    spawn(?MODULE, echo, [self()]).

test_tear_down(Server) ->
        eunit:debug({'teardown', self()}),
        Server ! stop.

test_one(Server) ->
    eunit:debug({'test one', self()}),
    assert_echo(Server, 'Hello').

test_two(Server) ->
    eunit:debug({'test two', self()}),
    assert_echo(Server, 'Hello2').

assert_echo(Server, Msg) ->
    Server ! Msg,
    receive
        {echo, Msg} -> ok
    after ?TIMEOUT ->
        throw("Message expected")
    end.

instantiate_test(Fun) ->
    fun(Fixture) ->
        eunit:debug({'instantiate', self()}),
        ?_test(Fun(Fixture)) end.
</snip>

Output:
<snip>
*eunit debug*: {instantiate,<0.48.0>}
*eunit debug*: {instantiate,<0.48.0>}
*eunit debug*: {setup,<0.284.0>}
*eunit debug*: {instantiate,<0.284.0>}
*eunit debug*: {'test one',<0.284.0>}
*eunit debug*: {echo,<0.284.0>,'Hello'}
*eunit debug*: {teardown,<0.284.0>}
*eunit debug*: {setup,<0.284.0>}
*eunit debug*: {instantiate,<0.284.0>}
*eunit debug*: {'test two',<0.288.0>}  <-- another process?!
*eunit debug*: {echo,<0.284.0>,'Hello2'}
fixture_sample:55:instantiate_test...*eunit debug*: {teardown,<0.284.0>}
*failed*
::throw:"Message expected"
  in function fixture_sample:assert_echo/2
</snip>



More information about the erlang-questions mailing list