mockgyver icon indicating copy to clipboard operation
mockgyver copied to clipboard

How to define a sequence of different replies from a mocked functions?

Open drapostolos opened this issue 10 years ago • 5 comments

I'm mocking a function something like this:

 ?WHEN(m:f(a, b) -> ok),

The function I'm testing calls the mocked function multiple times. With the mock above, the return value is always 'ok'. Is there a way to define a sequence of different return values instead? Something like this:

 ?WHEN(m:f(a, b) -> ok;
       m:f(a, b) -> nok;
       m:f(a, b) -> ok),

Where the first call to m:f(a, b) returns 'ok', second call returns 'nok' and third call returns 'ok'.

drapostolos avatar Jun 02 '14 12:06 drapostolos

Hi,

What you wrote in the example would be the same as writing this erlang function in m.erl:

f(a, b) -> ok;
f(a, b) -> nok;
f(a, b) -> ok.

I.e: dead code.

What you can do is to redefine the reply of your function in runtime, i.e. something like this:

?WHEN(m:f(a, b) -> ok),
... run test that would expect f/2 to return ok ...
?WHEN(m:f(a, b) -> nok),
... run test that would expect f/2 to return nok ...
?WHEN(...),

Other than that there's no way for a mock in mockgyver to have a "state", except for using something like ets to store and update a state between runs (implemented in the test case itself).

I've thought about a use for being able to return different values on subsequent calls but so far the above pattern has been enough. Do you have a good use case for stateful mocks? If so, it would be interesting to see.

Cheers, Klas

klajo avatar Jun 02 '14 14:06 klajo

I'm testing a function foo/1, which internally calls ct_netconfc:open/1 and ct_netconfc:close_session/1 twice.

Something like :

foo(Foo) ->
    %% in a separate process
        ct_netconfc:open/1
        %% do things...
        ct_netconfc:close_session/1 

    %% in current process
        ct_netconfc:open/1
        %% do things...
        ct_netconfc:close_session/1 

I want to test the behavior of foo/1 when the second call to ct_netconfc:close_session/1 returns {error, error_reason()}, but the first ct_netconfc session closses with ok. It then would be handy to use stateful mocks, where first call to ct_netconfc:close_session/1 is mocked to return ok and the second call returns {error, _}.

drapostolos avatar Jun 03 '14 08:06 drapostolos

I see. Is there a defined order between those two processes (the separate and current) or can those calls happen in any order? If any order: then it would be hard to solve with a mock.

Right now there's no support for this in mockgyver, except for writing the code for it yourself in the test case (eg. letting the mock update some ets table or something like that).

I'm flagging this as a feature request, but I will unfortunately not have the time to implement it in the near future.

/Klas

klajo avatar Jun 03 '14 14:06 klajo

Yes, the order is defined, where the netconf session in the "other" process happens before the netconf session in current process.

I found a workaround though, to mock away the spawning of the "other" process so it never happens, as i'm not interested in that.

/Alex

drapostolos avatar Jun 03 '14 15:06 drapostolos

Ok. There may be a risk of mocking the spawn, depending how it's done. If it's a "system" call (eg. proc_lib:spawn_link, gen_tcp:send, io:format, ...) that's commonly used by other modules/processes in the system - especially ones within Erlang/OTP itself - you may risk affecting the behaviour of the entire system.

I've written about this in the README: https://github.com/klajo/mockgyver#caveats

klajo avatar Jun 03 '14 15:06 klajo