From 544985b67032fdf5e7d563d8ba9f00c517db5a39 Mon Sep 17 00:00:00 2001 From: Ilia Choly Date: Fri, 26 Jun 2026 10:42:55 -0400 Subject: [PATCH] feat: make sinon.restoreObject idempotent Passing an object with no live fakes to restoreObject now restores nothing instead of throwing, giving it symmetry with sinon.restore() and sandbox.restore(). The strict "found no methods" check is retained for spy(object) and stub(object) via a new strict flag on walkObject that defaults to true. Passing a falsy value still throws. Fixes #2736 --- src/sinon/restore-object.js | 2 +- src/sinon/util/core/walk-object.js | 5 +++-- test/src/restore-object-test.js | 22 +++++++++------------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/sinon/restore-object.js b/src/sinon/restore-object.js index 08fdd4d5d..e47abf76b 100644 --- a/src/sinon/restore-object.js +++ b/src/sinon/restore-object.js @@ -15,5 +15,5 @@ export default function restoreObject(object) { ); } - return walkObject(restore, object, filter); + return walkObject(restore, object, filter, false); } diff --git a/src/sinon/util/core/walk-object.js b/src/sinon/util/core/walk-object.js index 31237ea60..b7278f994 100644 --- a/src/sinon/util/core/walk-object.js +++ b/src/sinon/util/core/walk-object.js @@ -24,9 +24,10 @@ import walk from "./walk.js"; * @param {ObjectMutator} mutator called on each property * @param {object} object the object we are walking over * @param {ObjectFilter} filter a predicate (boolean function) that will decide whether or not to apply the mutator to the current property + * @param {boolean} [strict] when true (default), throws if the mutator was never applied to any property * @returns {void} nothing */ -const walkObject = function (mutator, object, filter) { +const walkObject = function (mutator, object, filter, strict = true) { let called = false; const name = functionName(mutator); @@ -56,7 +57,7 @@ const walkObject = function (mutator, object, filter) { } }); - if (!called) { + if (!called && strict) { throw new Error( `Found no methods on object to which we could apply mutations`, ); diff --git a/test/src/restore-object-test.js b/test/src/restore-object-test.js index 1e291c08f..40d9215c3 100644 --- a/test/src/restore-object-test.js +++ b/test/src/restore-object-test.js @@ -35,19 +35,15 @@ describe("restore-object", function () { ); }); - it("throws with no spies or stubs", function () { - assert.exception( - function () { - restoreObject({ - catpants: function () {}, - meh: "okay", - }); - }, - { - message: - "Found no methods on object to which we could apply mutations", - }, - ); + it("is a no-op with no spies or stubs", function () { + const object = { + catpants: function () {}, + meh: "okay", + }; + + refute.exception(function () { + restoreObject(object); + }); }); it("works with mixed spies and stubs", function () {