[erlang-questions] Unit Testing gen_server callbacks

Daniel Eliasson <>
Tue Aug 14 10:53:47 CEST 2012


I guess it depends a bit on how advanced your server's state is. In the
adder example above, it's easy enough to test via the public interface.
However, if you want to test edge cases on the state, it can be a lot
easier to be able to just pass in the state you want to begin from, than to
start up a gen_server and send it the right sequence of commands to bring
it to that state.

I've been working on tests of a connection pool system, and it's a lot
easier to just test that handle_call, handle_info et. al. do the right
thing given an input state and a command, than it is to set up the
gen_server and provoke the right kind of errors via mocking.

Another way is to make the handle_call etc. simply destructure their input
and pass on to other functions that actually perform the operations. Then
you can unit test those functions separately. This might help avoid some of
the brittleness mentioned in regards to changing the formats of calls or
state.

/Daniel

On 14 August 2012 10:11, Steve Strong <> wrote:

> I would much prefer the latter - i.e., to test the public interface.
>  Otherwise you will end up with brittle tests that become a maintenance
> headache.  That said, I have had occasions where some functionality was
> much easier to test through the internal implementation rather than through
> the public API, so I think it depends to a degree on the context.
>
> Cheers,
>
> Steve
>
> --------------------------
> *Steve Strong*
> *Director, id3as*
> +34 636451137
> @srstrong
>
>
>
> On Aug 14, 2012, at 9:57 AM, Jan Vincent Liwanag <>
> wrote:
>
> Given the following code:
>
> adder.erl:
>
> start(B) -> gen_server:start(?MODULE, B, []).
> add(Pid, N) -> gen_server:cast(Pid, {add, N}).
> get_total(Pid, N) -> gen_server:call(Pid, get_total).
>
> init(Total) -> {ok, Total}.
>
> handle_call(get_total, _From, Total) -> {reply, Total, Total}.
> handle_cast({add, N}, Total) -> {noreply, N+Total).
>
>
> It would be better to have a test such that:
>
> {ok, P} = adder:start(0),
> adder:add(P, 5),
> adder:add(P, 3),
> adder:add(P, 2),
> ?assertEqual(10, adder:get_running_total()).
>
> Rather than:
>
> ?assertEqual({noreply, 12}, handle_cast({add, 5}, 7)).
> ?assertEqual({reply, 100, 100}, handle_call(get_total, somepid, 100)).
>
> For one thing, if I change the gen_server state from an int to a record
> and fix up the code - the first set of tests should still succeed while the
> second won't.
>
> On Aug 14, 2012, at 2:41 PM, Daniel Eliasson wrote:
>
> It should be easy enough to unit test such things, just run a handle_call
> with the right arguments, and verify that the output and new state is
> correct?
>
> I don't think there's a need to actually start the gen_server itself and
> unit test the callbacks by going through gen_server:call, if that's what
> you mean. In what way do you find it to be fragile?
>
> Best,
> Daniel
>
> On 14 August 2012 03:48, Jan Vincent Liwanag <> wrote:
>
>> Hi,
>>
>> What are your thoughts on unit testing gen_server (or other gen_*)
>> callbacks (handle_call, etc) directly? Is this an ok practice? Or should
>> unit tests test only the public api?
>>
>> On my end, I find testing gen_server callbacks to be rather fragile.
>> Eager to hear out other thoughts.
>>
>> Thanks,
>>
>>  Jan Vincent Liwanag
>> 
>> +63 (999) 888-0247
>>
>>
>> _______________________________________________
>> erlang-questions mailing list
>> 
>> http://erlang.org/mailman/listinfo/erlang-questions
>>
>>
>
> Jan Vincent Liwanag
> 
> +63 (999) 888-0247
>
> _______________________________________________
> erlang-questions mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-questions
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20120814/f89b0679/attachment.html>


More information about the erlang-questions mailing list