eproxus / meck

A mocking library for Erlang
http://eproxus.github.io/meck
Apache License 2.0
811 stars 231 forks source link

Promote meck_history:result to meck API...? #249

Open rogerl-happening opened 1 month ago

rogerl-happening commented 1 month ago

I was looking for a way to grab the result of a mocked call. Currently, I'm just peeking into meck:history().

So I went to implement something like meck:capture(..., result), when I noticed that meck_history:result() exists. It was added in https://github.com/eproxus/meck/pull/163.

But it was demoted from the top-level meck API. I think this was a mistake -- it ought to be at the same level of abstraction as meck:capture.

Can we put it back? Or can we make meck:capture take ArgNum | result...?

eproxus commented 1 month ago

Yeah, good point. There is a risk of polluting the API with a lot of very specific functions that could perhaps be generalized a bit.

What do you think about adding a new function that somehow grabs a function call from the history using some nice API (like capture/5,6 or similar?) and then returns a tuple with {Args, Result} or something?

rogerl-happening commented 1 month ago

I'm not quite sure what you're suggesting here.

Are you suggesting adding (for the sake of argument) meck:history(Occur, Mod, Func, OptArgsSpec)? That would call into (e.g.) meck_history:lookup. There's some common code in meck_history:capture() and meck_history:result() that does that, so they could both use lookup() to reduce duplication.

Or something else?

I quite like the meck:capture(..., result) thing (see #250), precisely because it adds a tiny amount of API surface.

eproxus commented 1 month ago

I guess I'm arguing for soft-deprecating capture in favor of something more generic.

I see it as meck:history/1+2 being the firehose, and capture/5+6 is a very specific subset of that ("give me one call and that particular argument value from it). It feels like there's something missing in-between, like "give me the arguments and return value from 'that particular call'", if that makes sense.

meck:history().
% [{<0.332.0>,{foo,bar,[a,b]},ok},
% {<0.332.0>,{foo,bar,[c,d]},ok},
% {<0.332.0>,
%  {foo,baz,[wat]},
%  error,some_error,
%  [{meck_ret_spec,eval_result,4,[{file,"src/meck_ret_spec.erl"},{line,93}]},
%   {foo,baz,[wat]}]},
% {<0.345.0>,{foo,bar,[x,y]},ok}]

meck:calls(foo, bar, 2).
% [{<0.332.0>,{foo,bar,[a,b]},ok},
% {<0.332.0>,{foo,bar,[c,d]},ok},
% {<0.345.0>,{foo,bar,[x,y]},ok}]

meck:call(foo, bar, 2, last).
% {<0.345.0>,{foo,bar,[x,y]},ok}

meck:call(foo, bar, 2, first).
% {<0.345.0>,{foo,bar,[a,b]},ok}

meck:calls(foo, bar, ['_', d])
% [{<0.332.0>,{foo,bar,[c,d]},ok}]
meck:call(foo, bar, ['_', d]) % implies 'last'
% {<0.345.0>,{foo,bar,[x,y]},ok}

(Totally made up proposal, so names and arguments are not that seriously suggested, yet).

With something like that, if you want the first argument and the result you could do:

{_From, [First|_], Result} = meck:call(foo, bar, ['_', d])

To get the nth argument from the argument list, you could use lists:nth/2 for example.

eproxus commented 1 month ago

Another long-standing idea I have for a new version of the history API is to include timestamps too, but maybe this is scope creep at this time ☺️