[erlang-questions] testing asynchronous code

Scott Lystig Fritchie fritchie@REDACTED
Tue Apr 20 22:37:40 CEST 2010


Martin DeMello <martindemello@REDACTED> wrote:

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

md> Thanks, some good ideas in there. Not sure I've absorbed it fully,
md> but I'll stare at it till it clicks :) From what I can make out,
md> though, it doesn't handle the case of handle_cast() itself calling
md> cast(), since the intermediate states before the entire tree of
md> casts has completed aren't really of interest. Is that correct, or
md> have I missed something?

You may or may not have missed something, it's tough to tell.  If your
test code calls YourModule:handle_cast() and checks the return result,
that checking is easiest if YourModule:handle_cast() is purely
functional.  If it has side-effects, then checking for sanity is more
difficult.  As John Hughes suggested, trace-based tests might be easier
if you're dealing with lots of side-effects.

It's quite unusual to have a gen_server send itself a message using
gen_server:cast().  I don't know why your code would do it...

... but I confess that it is very occasionally useful to use
gen_server:cast() or "self() ! {some_message_tag, ReminderData}" to
remind yourself to do something that is very inconvenient to deal with
at this instant in time.  When your call stack pops the normal way and
returns control to gen_server's message handling loop, then you can
conveniently handle that reminder data via YourModule:handle_cast() or
YourModule:handle_info().

    i_am_buried_seven_levels_deep_in_my_call_stack(Args, State) ->
        %% Weird args handling stuff can be better handled by sending
	%% a (non-blocking!) message to myself.  Perhaps this reminder
	%% message can look the same as a cast message that I have to
        %% handle anyway.
	%%
	%% One case where this is useful is if this function needs to
	%% modify "State", but the structure of the code (including all
	%% the intermediate callers between here and handle_cast() is
	%% inconvenient to refactor to return a new state var.
	%% Refactoring is usually the better choice ... except when
	%% it isn't better.  It depends.
        case is_arg_weird(Args, State) of
            true  -> gen_server:cast(self(), {arg_was_weird, Args});
	    false -> ok
	end,
	%% Do normal, down-in-the-depths calculation here, then return
	%% only an atom.
	ok.	

It's like a continuation, except uglier, except when it's only slightly
ugly.  Depends on the context.  :-)

-Scott


More information about the erlang-questions mailing list