Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add meck:get_state/0, meck:reset_state/0 #125

Closed
rlipscombe opened this issue Jun 18, 2014 · 7 comments
Closed

Add meck:get_state/0, meck:reset_state/0 #125

rlipscombe opened this issue Jun 18, 2014 · 7 comments

Comments

@rlipscombe
Copy link
Contributor

meck:new and meck:unload are quite slow. I attempted to use fixture setup and cleanup in eunit to only call them once for the fixture:

{setup, fun fixture_setup/0, fun fixture_cleanup/1,
  {foreach, fun case_setup/0, fun case_cleanup/1,
    Tests}}.

But then I have difficulty deleting expectations in case_cleanup: if I implement meck:delete/0, then it also removes expectations that were set up in fixture_setup and case_setup.

In particular passthrough and stub_all expectations.

So, I figure it'd be nice to be able to return an opaque term representing the current expectations at the end of case_setup. EUnit conveniently passes this to case_cleanup, where it could be restored with meck:reset_state/0.

This would save the slow meck:new and meck:unload on each case, but would guarantee that the state of the mocks was restored for the next test.

@eproxus
Copy link
Owner

eproxus commented Jun 23, 2014

With state of the mocks, do you only mean the validation state (meck:validate/1)?

@rlipscombe
Copy link
Contributor Author

No, I mean the entire state. I'd like (if possible) to pickle and unpickle the entire state of all extant mocks -- stubs, mocks and fakes. Not necessarily expectations.

Let me take a step back here. The core problem is that meck:new is slow, so I'd like to avoid calling it for every case_setup.

However, the only way for case_cleanup to erase any mocks set up in the individual tests is by using meck:unload().

There is meck:delete, but that requires knowing which mocks to delete. So I implemented a meck_extras:delete_all. This required some deep poking around with sys:get_state, which is evil, so I can submit a PR for it, if that sounds like something that should be in core meck (where it won't need to be evil).

But that removes any [passthrough] or [stub_all] expectations, which can only be set in meck:new. And we're trying to avoid calling meck:new.

I guess, instead of saving/restoring state, meck:delete/0, meck:reset/0, meck:passthrough/1 and meck:stub_all/1 would be sufficient.

Then I could have:

fixture_setup() ->
    meck:new(Mods).
fixture_cleanup() ->
    meck:unload().
case_setup() ->
    meck:stub_all(ModsToStub),
    meck:passthrough(ModsToPassThrough),
    meck:expect(OtherStuff).
case_cleanup() ->
    meck:reset(),
    meck:delete().

@rlipscombe
Copy link
Contributor Author

Oh, and the problem I found when attempting to write meck:stub_all(Mods) and meck:passthrough(Mods) is that, once you've called meck:new(Mod) there doesn't appear to be a way to get the exported functions any more.

@eproxus
Copy link
Owner

eproxus commented Jul 1, 2014

Ok, I see what you mean, I think.

When creating a mock, a new module is compiled (and the state is saved in the Meck process). When adding functions, passthroughs etc. the whole module is recompiled (since there is no way to add functions to an existing module in Erlang). This leads to that every new and expect call recompiles the module (which takes some time).

I suspect what you proposed would be equally slow to calling new, because everything will result in one or several recompilations anyway (new does some other things, like taking care of coverage etc., but that shouldn't be the major time consumption).

@eproxus
Copy link
Owner

eproxus commented Nov 21, 2016

Closing and documenting in the wiki instead: https://github.com/eproxus/meck/wiki#feature-ideas

Please comment here if you have information or requests to add, and/or want to discuss a PR or possible implementation proposal.

@eproxus eproxus closed this as completed Nov 21, 2016
@rlipscombe
Copy link
Contributor Author

I have an alternative, related, suggestion:

We have a lot of fixture setup of the form:

meck:new(foo),
meck:expect(foo, bar, fun(A, B) -> Whatever end),
meck:expect(foo, baz, fun(C) -> Whatever end),
meck:expect(foo, quux, fun(Q) -> Whatever end),
meck:expect(foo, wurdle, fun(X, Y) -> Whatever end),

This requires recompiling the module on each meck:expect call. Would it be possible to add a way to add multiple expectations in one go, and only compiling once? Or maybe there is, and I missed it? Should I raise another issue for this?

@eproxus
Copy link
Owner

eproxus commented Nov 21, 2016

Yeah, this is something I thought about as well, to have mock templates that compile everything in one go. Good point.

It's not possible at the moment, only different clauses for the same function at the same time. It's something I want to do for 2.0-something, to make the expects API more coherent and simpler (basically a tree structure of mod/func/clause, which would also make templating easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants