Skip to content

callsFake() on mocks only returns a value the first time #2663

@Anonymous-Coward

Description

@Anonymous-Coward

Describe the bug

If multiple distinct expectations, for multiple calls to the same mock, are set, and callsFake() is used on each of them, while the fake functions do get called, the return value is discarded for all of them but the first one.

To Reproduce

	it("Does not return all values from fake functions for mocks", function() {
		obj = {
			accumulator: [],
			accumulate: function(thing) {
				this.accumulator.push(thing);
				return `added ${thing}`;
			}
		}

		mock = sinon.mock(obj);

		mock.expects("accumulate")
			.withArgs("first thing").onCall(0).callsFake(thing => "mock-added first thing"); // .returns("mock-added first thing");
		mock.expects("accumulate")
			.withArgs("second thing").onCall(1).callsFake(thing => "mock-added second thing"); // .returns("mock-added second thing");
		mock.expects("accumulate")
			.withArgs("third thing").onCall(2).callsFake(thing => "mock-added third thing"); // .returns("mock-added third thing");

		expect(obj.accumulator).to.have.lengthOf(0);

		expect(obj.accumulate("first thing")).to.equal("mock-added first thing");
		expect(obj.accumulate("second thing")).to.equal("mock-added second thing");
		expect(obj.accumulate("third thing")).to.equal("mock-added third thing");

		mock.verify();
	});

Expected behavior

All expectations in the three lines before mock.verify() that verify calls to obj.accumulate() should pass, while obj.accumulator should stay empty.

Actual behavior

obj.accumulator does stay empty, because the first expect(...) statement passes. But the test fails in the second expect(...).to.equal(...) line, with this error message:

AssertionError: expected undefined to equal 'mock-added second thing'

If the commented out .returns(...) fragments are enabled, the test passes.

What's odd and inconsistent, however, is that if only the last two .returns(...) fragments are enabled, while the first one is left commented out, the test still passes. So the return value of the first callsFake(...) is returned, but the return values of subsequent calls to fake functions installed by callsFake() are discarded, and require an explicit additional returns(...) for a return value to be returned from the mocked call.

Screenshots

n.a.

Context (please complete the following information):

  • Sinon version : 17.0.1
  • Runtime: Node v22.19.0
    • Output of npx envinfo --browsers --binaries:
  Binaries:
    Node: 22.19.0 - /usr/bin/node
    npm: 10.9.2 - ~/KBV/src/kbv-devops/certs-updater/node_modules/.bin/npm
  Browsers:
    Chrome: 140.0.7339.127
  • Other relevant environmental info:
  • Other libraries you are using: Chai 4.5.0, mocha 11.0.1
  • Example URL: n.a.

Additional context

n.a.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions