[erlang-questions] QuickCheck/PropEr

Scott Lystig Fritchie fritchie@REDACTED
Wed May 25 19:54:34 CEST 2011


Edmond Begumisa <ebegumisa@REDACTED> wrote:

eb> This looks like a good case-study. I will analyse it fully. Quick
eb> one: are there any properties on gen_servers in here?

Specifically for gen_server, hrm, probably not.  But it isn't all that
difficult to do.

prop_server_doesnt_crash(InitArgs) ->
    ?FORALL(Commands, gen_my_servers_commands(),
            begin
                {ok, Pid} = my_server:start(InitArgs),
                [_ = apply(Mod, Func, Args) || {Mod, Fun, Args} <- Commands],
                Result = erlang:is_process_alive(Pid),
                catch my_server:please_stop_now(Pid),
                Result
            end).

If you want to check if the server is really sane, add a
testing-use-only command to fetch the server's internal state and
replace the last 3 lines with:

    FinalState = my_server:get_internal_state(Pid),
    my_server:please_stop_now(Pid),
    verify_sanity(FinalState)  %% returns boolean()

The hassle of cleaning up after each test (killing the process under
test) is a pain(*).  As Joe Norton pointed out, there's nothing that
prevents you from testing calls directly against your behavior's
callback module, i.e. calling my_server:handle_call/3 directly; you'll
have to do something more fold-like than the simple list-comprehension-
with-apply in the above example, but it's easy enough to do.

When testing via behavior callback functions, you'll only be testing the
server-side code and not any client-side code (e.g. calculations done
before a gen_server:call(), gen_server:cast(), or '!').  That might be
good, might be not good enough, depends on the purpose of the tests.

-Scott

(*) Testing code with any side-effects at all can cause difficulty.  In
the example below, just creating a new process is certainly a
side-effect.  Many gen_servers register a process name; if your test
framework doesn't clean up those processes 100% correctly(**), then
later tests can fail because a call to erlang:register/2 fails.

(**) Of course, that's also a problem when using EUnit, or your own test
tools, etc.



More information about the erlang-questions mailing list