Skip to content

Incorrect returnValue when using withArgs() and firstCall or lastCall #1487

@jvanoostveen

Description

@jvanoostveen
  • Sinon version : 2.3.8 (likely since 2.3.6)
  • Environment : Windows 10, Node 8.1.4
  • Other libraries you are using: sinon-chai

Related to: #1478 and #1476

What did you expect to happen?
It should not matter how the stub is queried, i.e., getCall(0), firstCall or lastCall should all return the same value if there is only one call using withArgs().
In version 2.3.5, this test succeeds, but when updating to any version above it (tested it with 2.3.8), the test will fail.

What actually happens
The test fails when using withArgs() together with firstCall.returnValue or lastCall.returnValue. But somehow, when using getCalls(0).returnValue or returnValues[0], it does work.

How to reproduce
Given the following test case (sorry, it's in TypeScript).
The inject stub will return a different fake component function based on the arguments that are passed. The return value of the inject stub is then used for example to check the arguments passed etc.

import { SinonStub } from 'sinon';
import * as chai from 'chai';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';

chai.use(sinonChai);
const { expect } = chai;

let testComponentA = (config: object) => ({ render: () => 'test a' });
let testComponentB = (config: object) => ({ render: () => 'test b' });

describe('Sinon firstCall, lastCall test', () => {
  let inject: SinonStub;

  beforeEach(() => {
    let fakeComponent = (variant: string) => {
      return sinon.stub().returns({
        render: sinon.stub().returns(`fake component ${variant}`)
      });
    };
    inject = sinon.stub().throws('Nothing set');
    inject.withArgs(testComponentA).returns(fakeComponent('a'));
    inject.withArgs(testComponentB).returns(fakeComponent('b'));
  });

  it('returnValues', () => {
    let config = { option: 'a' };
    let component = inject(testComponentA)(config);

    expect(inject).to.have.been.calledWith(testComponentA);
    expect(inject).to.not.have.been.calledWith(testComponentB);

    expect(component).to.respondTo('render');
    expect(component.render()).to.equal('fake component a');

    expect(inject.withArgs(testComponentA).returnValues[0]).to.have.been.calledWith(config); // Ok
    expect(inject.withArgs(testComponentA).getCall(0).returnValue).to.have.been.calledWith(config); // Ok

    expect(inject.withArgs(testComponentA).firstCall.returnValue).to.have.been.calledWith(config); // Fails
    expect(inject.withArgs(testComponentA).lastCall.returnValue).to.have.been.calledWith(config); // Fails
    // TypeError: undefined is not a spy or a call to a spy!
    // at assertCanWorkWith (node_modules\sinon-chai\lib\sinon-chai.js:53:19)
    // at Proxy.<anonymous> (node_modules\sinon-chai\lib\sinon-chai.js:98:13)
    // at Proxy.methodWrapper (node_modules\chai\lib\chai\utils\addMethod.js:57:25)
    // at Context.it (sinon-test.ts:39:79)
  });
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions