question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Check on spy call order by parameter

See original GitHub issue

In some cases is needed to check that the same spy has been called multiple times with different parameters (eg: while testing a curried function)

This is a possible desired feature, suggested by @keithamus:

const spy = chai.spy();
spy('a');
spy('b');
spy('c');
expect(spy).to.first.be.called.with('a');
expect(spy).to.second.be.called.with('b');
expect(spy).to.third.be.called.with('c');

It could be hard to match/generate first, second, third, nth property in the assertion chain.

Another possible assertion structure is:

const spy = chai.spy();
spy('a');
spy('b');
spy('c');
expect(spy).to.have.been.called.with.ordered('a', 'b', 'c');

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:1
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
keithamuscommented, Jun 27, 2017

We store the calls as an array of arguments, so this seems like an entirely reasonable feature to add. I’d personally like to see this feature as well. In addition I’d like to see:

const spy = chai.spy();
spy('a')
spy('b')
spy('c')
expect(spy).to.be.called.with('b').after.being.called.with('a')

Perhaps that’s a discussion for another time though.


The change:

Adding the flags first, second and third and modifying the with assertion to accept those flags. As an optional stretch goal, adding an after flag

Who can make this PR?

Anyone who wants to contribute to this project is welcome to make this PR!

I want to make this PR!

Great! You should take a look at our Code of Conduct and Contribution Guidelines (note: we need to work on our contribution guidelines, so these might not apply exactly to this sub-project).

If you want to work on this, you should comment below, something like:

Hey, I’d like to work on this issue, I’ll be making a PR shortly.

If you don’t comment you might run the risk of someone else doing the work before you!

If someone has already commented, then you should leave this PR for them to work on.

If someone has commented, but a few weeks have passed - it is worth asking the person who has commented if they’re still interested in working on this, and if you can pick it up instead.

How to make a PR for this change:

You’ll need to add the flags first, second, third - take a look at the always property to see how that’s done. You could do something like:

  Assertion.addProperty('first', function () {
    if ('undefined' !== this._obj.__spy) {
      _.flag(this, 'spy call index', 1);
    }
  });

  Assertion.addProperty('second', function () {
    if ('undefined' !== this._obj.__spy) {
      _.flag(this, 'spy call index', 2);
    }
  });

  Assertion.addProperty('third', function () {
    if ('undefined' !== this._obj.__spy) {
      _.flag(this, 'spy call index', 3);
    }
  });

You’ll then need to modify the .with function to listen to those flags. I’d recommend storing an index as part of the calls.forEach and then checking if the index flag exist, and if those two match - so assertWith becomes something like:

function assertWith () {
    new Assertion(this._obj).to.be.spy;
    var expArgs = [].slice.call(arguments, 0)
      , calls = this._obj.__spy.calls
      , always = _.flag(this, 'spy always')
      , expectedCallIndex = _.flag(this, 'spy call index')
      , callIndex = null
      , passed = 0;

    calls.forEach(function (call, index) {
      var actArgs = call.slice()
        , found = 0;

      expArgs.forEach(function (expArg) {
        for (var i = 0; i < actArgs.length; i++) {
          if (_.eql(actArgs[i], expArg)) {
            found++;
            actArgs.splice(i, 1);
            break;
          }
        }
      });
      if (found === expArgs.length) {
        passed++;
        callIndex = callIndex === null ? index : callIndex;
      }
    });

    if (typeof expectedCallIndex === 'number') {
      var extraMessage = 'was not called'
      if (callIndex >= 0) {
        extraMessage = 'was called on call ' + callIndex
      }
      this.assert(
        expectedCallIndex === callIndex
      , 'expected ' + this._obj + ' to have been called with #{exp} on call ' + expectedCallIndex + ' but ' + extraMessage,
      , 'expected ' + this._obj + ' to not have been called with #{exp} on call ' + expectedCallindex
      , expArgs
      )
    } else if (always) {
      this.assert(
          passed === calls.length
        , 'expected ' + this._obj + ' to have been always called with #{exp} but got ' + passed + ' out of ' + calls.length
        , 'expected ' + this._obj + ' to have not always been called with #{exp}'
        , expArgs
      );
    } else {
      this.assert(
          passed > 0
        , 'expected ' + this._obj + ' to have been called with #{exp}'
        , 'expected ' + this._obj + ' to have not been called with #{exp} but got ' + passed + ' times'
        , expArgs
      );
    }
  }

As always, you’ll need to write some tests to back this up and also add some docs in the README. If you want to implement an after flag as I mentioned above, please also feel free 😄. If you’d like some assistance with this - please feel free to ask!

Once if you done this, if you run the npm test command then you should see it all passes. If you get stuck, just pop on here to explain the problem and ping the @chaijs/chai-spies team, who will try to help with the issue.

What happens then?

We’ll need 2 contributors to accept the change, and it’ll be merged. Once merged, we’ll need to take some time to make a manual release, which will take a while - as we need to prepare release notes. It’ll likely be released as part of a set of changes for a major chai-spies version.

0reactions
stalniycommented, Sep 5, 2017

closed by #75

Read more comments on GitHub >

github_iconTop Results From Across the Web

Verifying function call and inspecting arguments using sinon ...
Is there any way to check that the method is called? Perhaps even extracting the arguments used in the bar() call? var spy...
Read more >
Spies - Sinon.JS
A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its...
Read more >
Testing Angular Components with Stub Services and Spies in ...
Testing proper sequence of function calls using spy. We can test which functions were called and with what parameters were they called using:...
Read more >
SpyProcedure • tSQLt - Database Unit Testing for SQL Server
Here we'll want to test that the SalesReport procedure handles the parameter correctly and calls either HistoricalReport or CurrentReport.
Read more >
Checking received calls - NSubstitute
Checking received calls · Check a call was not received · Check a call was received a specific number of times · Received...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found