[erlang-questions] testing asynchronous code

Scott Lystig Fritchie <>
Mon Apr 19 22:48:28 CEST 2010


Martin, you have another option: your test code calls
YourModule:handle_cast() directly.  If your client side stub looks like:

    client_side_stub(Server, Args) ->
        gen_server:cast(Server, {some_tag, Args}).

Then refactor to this and export both client_side_stub() and
make_some_tag().

    client_side_stub(Server, Args) ->
        gen_server:cast(Server, make_some_tag(Args)).

    make_some_tag(Args) ->
        {some_tag, Args}.

Now your testing code can do:

    OldState = YourModule:init(WhateverArgsYouNeed),
    case YourModule:handle_cast(YourModule:make_sometag(Args), OldState) of
        {noreply, State} ->
            verify_state_is_sane(State);
        {noreply, State, Timeout} ->
	    %% Your code may not use this return tuple...
            verify_state_is_sane(State);
        {stop, Reason, State} ->
	    %% Your code may not use this return tuple either
            verify_state_is_sane(State);

The above code is has lots of cut-and-paste redundancy that you can
factor out, if you wish.  Or you can use the skeleton as-is, allowing
the test code to check that the {noreply, State, Timeout} is indeed
correct in some cases and {noreply, State} correct in other cases.

There's only a little bit of glue code yet to write to grow into
something like this:

    OldState = YourModule:init(WhateverArgsYouNeed),
    State = fold_async_messages(YourModule, OldState,
                                [cast_message_number_1,
				 {cast_message_2, Args},
				 {cast_message_foo, Args, More, Stuff}]),
    ok = verify_state_is_sane(State).

Or perhaps you want to be able to pull the State out of an
already-running gen_server process and test it like the above.  Go
ahead, create this:

    handle_call(get_internal_state, _From, State) ->
        {reply, State, State};

Now your testing code can use
verify_state_is_sane(gen_server:call(Server, get_internal_state)).

Or perhaps you'd rather embed verify_state_is_sane() inside the
gen_server?  Your test code can do this call, but the rest of the client
API will ignore it.

    handle_call(check_internal_state, _From, State) ->
        ok = verify_internal_state(State),
        {reply, yup_looks_wonderful, State};

Hope this helps.

-Scott


More information about the erlang-questions mailing list