Search code examples
testingprocesserlangeunit

eunit: How to test a simple process?


I'm currently writing a test for a module that runs in a simple process started with spawn_link(?MODULE, init, [self()]).

In my eunit tests, I have a setup and teardown function defined and a set of test generators.

all_tests_test_() ->
    {inorder, {
        foreach,
        fun setup/0,
        fun teardown/1,
        [
            fun my_test/1
        ]}
    }.

The setup fun creates the process-under-test:

setup() ->
    {ok, Pid} = protocol:start_link(),
    process_flag(trap_exit,true),
    error_logger:info_msg("[~p] Setting up process ~p~n", [self(), Pid]),
    Pid.

The test looks like this:

my_test(Pid) ->
    [ fun() ->
            error_logger:info_msg("[~p] Sending to ~p~n", [self(), Pid]),
            Pid ! something,
            receive
                Msg -> ?assertMatch(expected_result, Msg)
            after
                500 -> ?assert(false)
            end
        end ].

Most of my modules are gen_server but for this I figured it'll be easier without all gen_server boilerplate code...

The output from the test looks like this:

=INFO REPORT==== 31-Mar-2014::21:20:12 ===
[<0.117.0>] Setting up process <0.122.0>

=INFO REPORT==== 31-Mar-2014::21:20:12 ===
[<0.124.0>] Sending to <0.122.0>

=INFO REPORT==== 31-Mar-2014::21:20:12 ===
[<0.122.0>] Sending expected_result to <0.117.0>
protocol_test: my_test...*failed*
in function protocol_test:'-my_test/1-fun-0-'/0 (test/protocol_test.erl, line 37)
**error:{assertion_failed,[{module,protocol_test},
                   {line,37},
                   {expression,"false"},
                   {expected,true},
                   {value,false}]}

From the Pids you can see that whatever process was running setup (117) was not the same that was running the test case (124). The process under test however is the same (122). This results in a failing test case because the receive never gets the message und runs into the timeout.

Is that the expected behaviour that a new process gets spawned by eunit to run the test case?

An generally, is there a better way to test a process or other asynchronous behaviour (like casts)? Or would you suggest to always use gen_server to have a synchronous interface?

Thanks!

[EDIT]

To clarify, how protocol knows about the process, this is the start_link/0 fun:

start_link() ->
    Pid = spawn_link(?MODULE, init, [self()]),
    {ok, Pid}.

The protocol ist tightly linked to the caller. If the either of them crashes I want the other one to die as well. I know I could use gen_server and supervisors and actually it did that in parts of the application, but for this module, I thought it was a bit over the top.


Solution

  • did you try:

    all_tests_test_() ->
        {inorder, {
            foreach,
            local,
            fun setup/0,
            fun teardown/1,
            [
                fun my_test/1
            ]}
        }.
    

    From the doc, it seems to be what you need.