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

Verify of a specific call when multiple calls happen #120

Closed
albertogasparin opened this issue Jul 21, 2016 · 3 comments
Closed

Verify of a specific call when multiple calls happen #120

albertogasparin opened this issue Jul 21, 2016 · 3 comments

Comments

@albertogasparin
Copy link

Is it possible to verify a specific call when the testdouble gets called multiple times?

var fn = td.func();
fn();
fn(1);
fn(2);
td.verify(fn(1)); // I'd like to check that this was the second call

I was not able to find this kind of example in the docs. Thanks!

@searls
Copy link
Member

searls commented Jul 21, 2016

What you're doing is possible, but the library doesn't (currently) do anything to make it easy because needing to do this is typically a smell that there's something wrong with the design. I'll explain why later.

First, to accomplish what you want, you can:

var fn = td.func();
fn();
fn(1);
fn(2);

assert.deepEqual(td.explain(fn).calls[1].args, [1])

Where td.explain(fn) will give you metadata on the double, and its calls array will expose what's happened to it so far. Then you can assert however you like.

td.js could implement an option for this: td.verify(fn(1), {onCall: 2}) but I'm not really in a rush tbh for reasons that follow:

Design smell

Put simply, this should be rarely needed IMO. Following this train of thought:

  1. verifications should be relatively rare because test-driving code that has side effects should be relatively rare.
  2. Multiple verifications of the same function should be even more rare, because whatever has the side effect should probably batch up its actions instead of leaking that responsibility up to the caller
  3. Multiple verifications of the same function in which order genuinely matters should be even rarer still, because it's just something you don't run into in practice very often (for instance, almost no I/O guarantees that things will happen in the order invoked, and I/O represents almost all side effects in everyday applications)

As a result, I've only seen a handful of cases in the last 7 years of using test doubles where this felt necessary and the design was also sound. Even now I'm cautious to say it's ever been a good idea, since it's been a while.

@searls searls closed this as completed Jul 21, 2016
@albertogasparin
Copy link
Author

Thanks for the explanation @searls. Using td.explain(fn).calls[n].args is an acceptable compromise if you don't like promoting this behaviour.

Just to give you a bit of contest, I agree that usually that pattern is not great, however it is a suggested one when using redux-thunk in a React + Redux project.

I don't know if you are familiar with Redux, but basically I call a method (action) from my React view and that method is wrapped in a thunk in order to update my data (store) asynchronously. The tricky bit is that in order to update different data points, I need to call the thunk first argument (the dispatcher, responsible of executing the update) multiple times:

// my view calls an enhanced version of this method
function incrementAsync() {
  // redux-thunk is responsible of executing and passing the arguments
  return (dispatch) => {
    setTimeout(() => {
      dispatch({ type: 'BLA', data: 'bla' });
      dispatch({ type: 'FOO', data: 'foo' });
      // and possibly many more
    }, 1000);
  };
}

So I have configured in my tests like this:

let thunk = actions.loadAsync();
let dispatch = td.func('dispatch');
thunk(dispatch);
td.verify(dispatch({ type: 'BLA', data: 'bla' }));
td.verify(dispatch({ type: 'FOO', data: 'foo' }));
// ...

Besides, not being able to specify on which call I was expecting type: 'BLA' makes the testing harder as td error message will list all the calls on the test double and not just the offending one (I love td error messages btw).

@searls
Copy link
Member

searls commented Jul 22, 2016

Thanks for replying with more detail. Unfortunately I don't know anything about redux-thunk and can't help you on the specifics. My only feedback would be to suggest that replacing third-party APIs with test doubles is generally considered counter-productive when unit testing or practicing TDD.

To explain at length I just wrote this wiki page on "Don't Mock What You Don't Own" for you

@searls searls reopened this Jul 22, 2016
@searls searls closed this as completed Jul 22, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants