From 40f5ca328a41e9fb6408ccfb3c732a6e3e4ba146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serkan=20=C3=96zel?= Date: Tue, 25 May 2021 10:56:27 +0300 Subject: [PATCH] Fake docs improvement (#2360) * Clarify fake api, add missing references to spy api * Mention return value in sandbox.replace() * Move fake changes to latest docs * Move the sandbox change to latest docs * Add fake runkit examples using existing spy examples * Fix pubsub fake example * Change runnable examples to be the same with the ones in the current documentation * Add examples, address some review comments --- ...kes-1-using-fakes-instead-of-spies.test.js | 17 +++++ .../examples/fakes-10-firstArg.test.js | 17 +++++ .../release/examples/fakes-11-lastArg.test.js | 21 ++++++ ...2-adding-fake-to-system-under-test.test.js | 20 ++++++ ...kes-2-using-fakes-instead-of-stubs.test.js | 21 ++++++ ...fakes-3-creating-without-behaviour.test.js | 14 ++++ ...s-4-creating-with-custom-behaviour.test.js | 13 ++++ .../release/examples/fakes-5-returns.test.js | 12 ++++ .../release/examples/fakes-6-throws.test.js | 13 ++++ .../release/examples/fakes-7-yields.test.js | 23 +++++++ .../examples/fakes-8-yields-async.test.js | 23 +++++++ .../release/examples/fakes-9-callback.test.js | 20 ++++++ docs/release-source/release/fakes.md | 64 +++++++++++++++++-- docs/release-source/release/sandbox.md | 2 +- 14 files changed, 275 insertions(+), 5 deletions(-) create mode 100644 docs/release-source/release/examples/fakes-1-using-fakes-instead-of-spies.test.js create mode 100644 docs/release-source/release/examples/fakes-10-firstArg.test.js create mode 100644 docs/release-source/release/examples/fakes-11-lastArg.test.js create mode 100644 docs/release-source/release/examples/fakes-12-adding-fake-to-system-under-test.test.js create mode 100644 docs/release-source/release/examples/fakes-2-using-fakes-instead-of-stubs.test.js create mode 100644 docs/release-source/release/examples/fakes-3-creating-without-behaviour.test.js create mode 100644 docs/release-source/release/examples/fakes-4-creating-with-custom-behaviour.test.js create mode 100644 docs/release-source/release/examples/fakes-5-returns.test.js create mode 100644 docs/release-source/release/examples/fakes-6-throws.test.js create mode 100644 docs/release-source/release/examples/fakes-7-yields.test.js create mode 100644 docs/release-source/release/examples/fakes-8-yields-async.test.js create mode 100644 docs/release-source/release/examples/fakes-9-callback.test.js diff --git a/docs/release-source/release/examples/fakes-1-using-fakes-instead-of-spies.test.js b/docs/release-source/release/examples/fakes-1-using-fakes-instead-of-spies.test.js new file mode 100644 index 000000000..ee495dbca --- /dev/null +++ b/docs/release-source/release/examples/fakes-1-using-fakes-instead-of-spies.test.js @@ -0,0 +1,17 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should be able to be used instead of spies", function () { + const foo = { + bar: () => "baz", + }; + // wrap existing method without changing its behaviour + const fake = sinon.replace(foo, "bar", sinon.fake(foo.bar)); + + assert.equals(fake(), "baz"); // behaviour is the same + assert.equals(fake.callCount, 1); // calling information is saved + }); +}); diff --git a/docs/release-source/release/examples/fakes-10-firstArg.test.js b/docs/release-source/release/examples/fakes-10-firstArg.test.js new file mode 100644 index 000000000..5b047bf0b --- /dev/null +++ b/docs/release-source/release/examples/fakes-10-firstArg.test.js @@ -0,0 +1,17 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should have working firstArg property", function () { + const f = sinon.fake(); + const date1 = new Date(); + const date2 = new Date(); + + f(date1, 1, 2); + f(date2, 1, 2); + + assert.isTrue(f.firstArg === date2); + }); +}); diff --git a/docs/release-source/release/examples/fakes-11-lastArg.test.js b/docs/release-source/release/examples/fakes-11-lastArg.test.js new file mode 100644 index 000000000..5cb877413 --- /dev/null +++ b/docs/release-source/release/examples/fakes-11-lastArg.test.js @@ -0,0 +1,21 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should have working lastArg property", function () { + const f = sinon.fake(); + const date1 = new Date(); + const date2 = new Date(); + + f(1, 2, date1); + f(1, 2, date2); + + assert.isTrue(f.lastArg === date2); + // spy call methods: + assert.isTrue(f.getCall(0).lastArg === date1); + assert.isTrue(f.getCall(1).lastArg === date2); + assert.isTrue(f.lastCall.lastArg === date2); + }); +}); diff --git a/docs/release-source/release/examples/fakes-12-adding-fake-to-system-under-test.test.js b/docs/release-source/release/examples/fakes-12-adding-fake-to-system-under-test.test.js new file mode 100644 index 000000000..9a5e18304 --- /dev/null +++ b/docs/release-source/release/examples/fakes-12-adding-fake-to-system-under-test.test.js @@ -0,0 +1,20 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should have working lastArg property", function () { + const fake = sinon.fake.returns("42"); + + sinon.replace(console, "log", fake); + + assert.equals(console.log("apple pie"), 42); + + // restores all replaced properties set by sinon methods (replace, spy, stub) + sinon.restore(); + + assert.equals(console.log("apple pie"), undefined); + assert.equals(fake.callCount, 1); + }); +}); diff --git a/docs/release-source/release/examples/fakes-2-using-fakes-instead-of-stubs.test.js b/docs/release-source/release/examples/fakes-2-using-fakes-instead-of-stubs.test.js new file mode 100644 index 000000000..04a272daf --- /dev/null +++ b/docs/release-source/release/examples/fakes-2-using-fakes-instead-of-stubs.test.js @@ -0,0 +1,21 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should be able to be used instead of stubs", function () { + const foo = { + bar: () => "baz", + }; + // replace method with a fake one + const fake = sinon.replace( + foo, + "bar", + sinon.fake.returns("fake value") + ); + + assert.equals(fake(), "fake value"); // returns fake value + assert.equals(fake.callCount, 1); // saves calling information + }); +}); diff --git a/docs/release-source/release/examples/fakes-3-creating-without-behaviour.test.js b/docs/release-source/release/examples/fakes-3-creating-without-behaviour.test.js new file mode 100644 index 000000000..51879e0df --- /dev/null +++ b/docs/release-source/release/examples/fakes-3-creating-without-behaviour.test.js @@ -0,0 +1,14 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should create fake without behaviour", function () { + // create a basic fake, with no behavior + const fake = sinon.fake(); + + assert.isUndefined(fake()); // by default returns undefined + assert.equals(fake.callCount, 1); // saves call information + }); +}); diff --git a/docs/release-source/release/examples/fakes-4-creating-with-custom-behaviour.test.js b/docs/release-source/release/examples/fakes-4-creating-with-custom-behaviour.test.js new file mode 100644 index 000000000..ebb769a8a --- /dev/null +++ b/docs/release-source/release/examples/fakes-4-creating-with-custom-behaviour.test.js @@ -0,0 +1,13 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should create fake with custom behaviour", function () { + // create a fake that returns the text "foo" + const fake = sinon.fake.returns("foo"); + + assert.equals(fake(), "foo"); + }); +}); diff --git a/docs/release-source/release/examples/fakes-5-returns.test.js b/docs/release-source/release/examples/fakes-5-returns.test.js new file mode 100644 index 000000000..092475d93 --- /dev/null +++ b/docs/release-source/release/examples/fakes-5-returns.test.js @@ -0,0 +1,12 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should create a fake that 'returns' a value", function () { + const fake = sinon.fake.returns("apple pie"); + + assert.equals(fake(), "apple pie"); + }); +}); diff --git a/docs/release-source/release/examples/fakes-6-throws.test.js b/docs/release-source/release/examples/fakes-6-throws.test.js new file mode 100644 index 000000000..a92c81edc --- /dev/null +++ b/docs/release-source/release/examples/fakes-6-throws.test.js @@ -0,0 +1,13 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should create a fake that 'throws' an Error", function () { + const fake = sinon.fake.throws(new Error("not apple pie")); + + // Expected to throw an error with message 'not apple pie' + assert.exception(fake, { name: "Error", message: "not apple pie" }); + }); +}); diff --git a/docs/release-source/release/examples/fakes-7-yields.test.js b/docs/release-source/release/examples/fakes-7-yields.test.js new file mode 100644 index 000000000..70b7aa3e8 --- /dev/null +++ b/docs/release-source/release/examples/fakes-7-yields.test.js @@ -0,0 +1,23 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; +const fs = require("fs"); + +describe("FakeTest", function () { + it("should create a fake that 'yields'", function () { + const fake = sinon.fake.yields(null, "file content"); + const anotherFake = sinon.fake(); + + sinon.replace(fs, "readFile", fake); + fs.readFile("somefile", (err, data) => { + // called with fake values given to yields as arguments + assert.isNull(err); + assert.equals(data, "file content"); + // since yields is synchronous, anotherFake is not called yet + assert.isFalse(anotherFake.called); + }); + + anotherFake(); + }); +}); diff --git a/docs/release-source/release/examples/fakes-8-yields-async.test.js b/docs/release-source/release/examples/fakes-8-yields-async.test.js new file mode 100644 index 000000000..1c8d16822 --- /dev/null +++ b/docs/release-source/release/examples/fakes-8-yields-async.test.js @@ -0,0 +1,23 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; +const fs = require("fs"); + +describe("FakeTest", function () { + it("should create a fake that 'yields asynchronously'", function () { + const fake = sinon.fake.yieldsAsync(null, "file content"); + const anotherFake = sinon.fake(); + + sinon.replace(fs, "readFile", fake); + fs.readFile("somefile", (err, data) => { + // called with fake values given to yields as arguments + assert.isNull(err); + assert.equals(data, "file content"); + // since yields is asynchronous, anotherFake is called first + assert.isTrue(anotherFake.called); + }); + + anotherFake(); + }); +}); diff --git a/docs/release-source/release/examples/fakes-9-callback.test.js b/docs/release-source/release/examples/fakes-9-callback.test.js new file mode 100644 index 000000000..129c30ea1 --- /dev/null +++ b/docs/release-source/release/examples/fakes-9-callback.test.js @@ -0,0 +1,20 @@ +require("@fatso83/mini-mocha").install(); +const sinon = require("sinon"); +const referee = require("@sinonjs/referee"); +const assert = referee.assert; + +describe("FakeTest", function () { + it("should have working callback property", function () { + const f = sinon.fake(); + const cb1 = function () {}; + const cb2 = function () {}; + + f(1, 2, 3, cb1); + f(1, 2, 3, cb2); + + assert.isTrue(f.callback === cb2); + // spy call methods: + assert.isTrue(f.getCall(1).callback === cb2); + assert.isTrue(f.lastCall.callback === cb2); + }); +}); diff --git a/docs/release-source/release/fakes.md b/docs/release-source/release/fakes.md index 424db61e6..bd365287f 100644 --- a/docs/release-source/release/fakes.md +++ b/docs/release-source/release/fakes.md @@ -6,7 +6,7 @@ breadcrumb: fakes ### Introduction -`fake` is available in Sinon from v5 onwards. It allows creation of a `fake` `Function` with the ability to set a default [behavior](#fakes-with-behavior). Set the [behavior](#fakes-with-behavior) using `Functions` with the same API as those in a [`sinon.stub`][stubs]. The created `fake` `Function`, with or without behavior has the same API as a (`sinon.spy`)[spies]. +`fake` is available in Sinon from v5 onwards. It allows creation of a `fake` `Function` with the ability to set a default [behavior](#fakes-with-behavior). The available [behaviors](#fakes-with-behavior) for the most part match the API of a [`sinon.stub`][stubs]. In Sinon, a `fake` is a `Function` that records arguments, return value, the value of `this` and exception thrown (if any) for all of its calls. @@ -14,6 +14,50 @@ A fake is immutable: once created, the behavior will not change. Unlike [`sinon.spy`][spies] and [`sinon.stub`][stubs] methods, the `sinon.fake` API knows only how to create fakes, and doesn't concern itself with plugging them into the system under test. To plug the fakes into the system under test, you can use the [`sinon.replace*`](../sandbox#sandboxreplaceobject-property-replacement) methods. +### When to use fakes? + +Fakes are alternatives to the Stubs and Spies, and they can fully replace all such use cases. + +They are intended to be simpler to use, and avoids many bugs by having immutable behaviour. + +The created `fake` `Function`, with or without behavior has the same API as a (`sinon.spy`)[spies]. + +#### Using fakes instead of spies + +```js +var foo = { + bar: () => { + return "baz"; + }, +}; +// wrap existing method without changing its behaviour +var fake = sinon.replace(foo, "bar", sinon.fake(foo.bar)); + +fake(); +// baz + +fake.callCount; +// 1 +``` + +#### Using fakes instead of stubs + +```js +var foo = { + bar: () => { + return "baz"; + }, +}; +// replace method with a fake one +var fake = sinon.replace(foo, "bar", sinon.fake.returns("fake value")); + +fake(); +// fake value + +fake.callCount; +// 1 +``` + ### Creating a fake Create a `fake` `Function` with or without [behavior](#fakes-with-behavior). The created `Function` has the same API as a [`sinon.spy`][spies]. @@ -123,7 +167,7 @@ This is useful when complex behavior not covered by the `sinon.fake.*` methods i ### Instance properties -The instance properties are the same as a [`sinon.spy`][spies]. +The instance properties are the same as those of a [`sinon.spy`][spies]. The following examples showcase just a few of the properties available to you. Refer to the [spy docs][spies] for a complete list. #### `f.callback` @@ -141,7 +185,7 @@ f.callback === cb2; // true ``` -The same convenience has been added to [spy calls](../spy-call): +The same convenience has been added to [spy calls](../spy-call#spycallcallback): ```js f.getCall(1).callback === cb2; @@ -167,6 +211,18 @@ f.firstArg === date2; // true ``` +The same convenience has been added to [spy calls](../spy-call#spycallfirstarg): + +```js +f.getCall(0).firstArg === date1; +// true +f.getCall(1).firstArg === date2; +// true +// +f.lastCall.firstArg === date2; +// true +``` + #### `f.lastArg` This property is a convenient way to get a reference to the last argument passed in the last call to the fake. @@ -183,7 +239,7 @@ f.lastArg === date2; // true ``` -The same convenience has been added to [spy calls](../spy-call): +The same convenience has been added to [spy calls](../spy-call#spycalllastarg): ```js f.getCall(0).lastArg === date1; diff --git a/docs/release-source/release/sandbox.md b/docs/release-source/release/sandbox.md index 8f346ff09..822c9ce4d 100644 --- a/docs/release-source/release/sandbox.md +++ b/docs/release-source/release/sandbox.md @@ -183,7 +183,7 @@ _Since `sinon@2.0.0`_ #### `sandbox.replace(object, property, replacement);` -Replaces `property` on `object` with `replacement` argument. Attempts to replace an already replaced value cause an exception. +Replaces `property` on `object` with `replacement` argument. Attempts to replace an already replaced value cause an exception. Returns the `replacement`. `replacement` can be any value, including `spies`, `stubs` and `fakes`.