diff --git a/docs/release-source/release/assertions.md b/docs/release-source/release/assertions.md index ace751ad5..163ca888e 100644 --- a/docs/release-source/release/assertions.md +++ b/docs/release-source/release/assertions.md @@ -87,6 +87,12 @@ Passes if `spy` was called with the provided arguments. It's possible to assert on a dedicated spy call: `sinon.assert.calledWith(spy.firstCall, arg1, arg2, ...);`. +#### `sinon.assert.calledOnceWith(spyOrSpyCall, arg1, arg2, ...);` + +Passes if `spy` was called once and only once with the provided arguments. + +It's possible to assert on a dedicated spy call: `sinon.assert.calledOnceWith(spy.firstCall, arg1, arg2, ...);`. + #### `sinon.assert.alwaysCalledWith(spy, arg1, arg2, ...);` Passes if `spy` was always called with the provided arguments. diff --git a/lib/sinon/assert.js b/lib/sinon/assert.js index 374be4d8b..58e6c3ab6 100644 --- a/lib/sinon/assert.js +++ b/lib/sinon/assert.js @@ -304,6 +304,10 @@ function createAssertObject(opts) { "calledWithExactly", "expected %n to be called with exact arguments %D", ); + mirrorPropAsAssertion( + "calledOnceWith", + "expected %n to be called once and with arguments %D", + ); mirrorPropAsAssertion( "calledOnceWithExactly", "expected %n to be called once and with exact arguments %D", diff --git a/test/assert-test.js b/test/assert-test.js index 101f439e1..a431f9920 100644 --- a/test/assert-test.js +++ b/test/assert-test.js @@ -841,6 +841,109 @@ describe("assert", function () { }); }); + describe(".calledOnceWith", function () { + // eslint-disable-next-line mocha/no-setup-in-describe + requiresValidFake("calledOnceWith"); + + it("fails when method fails", function () { + const object = {}; + sinonStub(this.stub, "calledOnceWith").returns(false); + const stub = this.stub; + + assert.exception(function () { + sinonAssert.calledOnceWith(stub, object, 1); + }); + + assert( + this.stub.calledOnceWith.calledOnceWithExactly(object, 1), + ); + assert(sinonAssert.fail.called); + }); + + it("passes when method doesn't fail", function () { + const object = {}; + sinonStub(this.stub, "calledOnceWith").returns(true); + const stub = this.stub; + + refute.exception(function () { + sinonAssert.calledOnceWith(stub, object, 1); + }); + + assert( + this.stub.calledOnceWith.calledOnceWithExactly(object, 1), + ); + assert.isFalse(sinonAssert.fail.called); + }); + + it("calls pass callback", function () { + this.stub("yeah"); + sinonAssert.calledOnceWith(this.stub, "yeah"); + + assert(sinonAssert.pass.calledOnce); + assert(sinonAssert.pass.calledWith("calledOnceWith")); + }); + + it("fails when method does not exist", function () { + assert.exception(function () { + sinonAssert.calledOnceWith(); + }); + + assert(sinonAssert.fail.called); + }); + + it("fails when method is not stub", function () { + assert.exception(function () { + sinonAssert.calledOnceWith(function () { + return; + }); + }); + + assert(sinonAssert.fail.called); + }); + + it("fails when method was not called", function () { + const stub = this.stub; + + assert.exception(function () { + sinonAssert.calledOnceWith(stub); + }); + + assert(sinonAssert.fail.called); + }); + + it("fails when called with more than one argument", function () { + const stub = this.stub; + stub(); + + assert.exception(function () { + sinonAssert.calledOnceWith(stub, 1); + }); + }); + + it("passes when method was called", function () { + const stub = this.stub; + stub(); + + refute.exception(function () { + sinonAssert.calledOnceWith(stub); + }); + + assert.isFalse(sinonAssert.fail.called); + }); + + it("fails when method was called more than once", function () { + const stub = this.stub; + stub(); + stub(); + + assert.exception(function () { + sinonAssert.calledOnceWith(stub); + }); + + assert(sinonAssert.fail.called); + }); + }); + describe(".calledOnceWithExactly", function () { // eslint-disable-next-line mocha/no-setup-in-describe requiresValidFake("calledOnceWithExactly");