sinon icon indicating copy to clipboard operation
sinon copied to clipboard

returnValue of calls is out of order when spying on recursive functions

Open justinlovinger opened this issue 6 years ago • 5 comments

Describe the bug

The return value, as given by spy.getCall(n).returnValue, is in the wrong order when spying on a recursive function. The return values are in the order that calls returned, instead of the order of the calls themselves. For example, the returnValue of the first call is the value returned by the last call, instead of the value returned by the first call.

Note that this bug occurs with both sinon.spy and sinon.fake.

To Reproduce

recurse.js

function foo(num) {
    if (num < 2) {
        foo(num + 1);
    }

    return num;
}

recurse.test.js

const rewire = require('rewire');
const expect = require('chai').expect;
const sinon = require('sinon');

const recurse = rewire('../recurse');


describe('foo', function () {
    it('should recursively add 1 but return original number', function () {
        recurse.__with__({
            foo: sinon.fake(recurse.__get__('foo'))
        })(function () {
            recurse.__get__('foo')(0);

            expect(recurse.__get__('foo').getCall(0).returnValue).to.equal(0);
            expect(recurse.__get__('foo').getCall(1).returnValue).to.equal(1);
            expect(recurse.__get__('foo').getCall(2).returnValue).to.equal(2);
        });
    });
});

Expected behavior

spy.getCall(n).returnValue should be the return value of the nth call to spy.

Context:

  • Library version: 6.0.1
  • Environment: Windows
  • Other libraries you are using: mocha, chai, rewire

justinlovinger avatar Jun 29 '18 04:06 justinlovinger

Hmm ... any idea of how to fix this?

fatso83 avatar Jun 30 '18 06:06 fatso83

I edited the description to add language annotations to the the fenced blocks to allow GFM to do syntax highlighting

mroderick avatar Jul 05 '18 21:07 mroderick

This doesn't work the way you think. The problem is the inner function has a closure so it never calls the (outer) fake in my testing. Is rewire fixing that somehow?

http://jsbin.com/sevesibibu/edit?js,console

I don't think this is a problem that sinon can resolve honestly.

fearphage avatar Jul 09 '18 04:07 fearphage

@fearphage Your example is not actually replacing the function foo with a fake. You are instead creating a new function that wraps foo, and calling that.

Compare to this:

let count = 0;

function foo(num) {
    console.log(`foo called: ${++count}; num: ${num}`);
  
    if (num < 5) {
        return num + foo(num + 1);
    }

    return num;
}

foo = sinon.fake(foo);

const result = foo(2);

console.log(`fake call count: ${foo.callCount}, result: ${result}`);

justinlovinger avatar Jul 09 '18 20:07 justinlovinger

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 07 '18 20:09 stale[bot]