@@ -352,6 +352,89 @@ Otherwise, the test is considered to be a failure. Test files must be
352352executable by Node.js, but are not required to use the ` node:test ` module
353353internally.
354354
355+ ## Mocking
356+
357+ The ` node:test ` module supports mocking during testing via a top-level ` mock `
358+ object. The following example creates a spy on a function that adds two numbers
359+ together. The spy is then used to assert that the function was called as
360+ expected.
361+
362+ ``` mjs
363+ import assert from ' node:assert' ;
364+ import { mock , test } from ' node:test' ;
365+
366+ test (' spies on a function' , () => {
367+ const sum = mock .fn ((a , b ) => {
368+ return a + b;
369+ });
370+
371+ assert .strictEqual (sum .mock .calls .length , 0 );
372+ assert .strictEqual (sum (3 , 4 ), 7 );
373+ assert .strictEqual (sum .mock .calls .length , 1 );
374+
375+ const call = sum .mock .calls [0 ];
376+ assert .deepStrictEqual (call .arguments , [3 , 4 ]);
377+ assert .strictEqual (call .result , 7 );
378+ assert .strictEqual (call .error , undefined );
379+
380+ // Reset the globally tracked mocks.
381+ mock .reset ();
382+ });
383+ ```
384+
385+ ``` cjs
386+ ' use strict' ;
387+ const assert = require (' node:assert' );
388+ const { mock , test } = require (' node:test' );
389+
390+ test (' spies on a function' , () => {
391+ const sum = mock .fn ((a , b ) => {
392+ return a + b;
393+ });
394+
395+ assert .strictEqual (sum .mock .calls .length , 0 );
396+ assert .strictEqual (sum (3 , 4 ), 7 );
397+ assert .strictEqual (sum .mock .calls .length , 1 );
398+
399+ const call = sum .mock .calls [0 ];
400+ assert .deepStrictEqual (call .arguments , [3 , 4 ]);
401+ assert .strictEqual (call .result , 7 );
402+ assert .strictEqual (call .error , undefined );
403+
404+ // Reset the globally tracked mocks.
405+ mock .reset ();
406+ });
407+ ```
408+
409+ The same mocking functionality is also exposed on the [ ` TestContext ` ] [ ] object
410+ of each test. The following example creates a spy on an object method using the
411+ API exposed on the ` TestContext ` . The benefit of mocking via the test context is
412+ that the test runner will automatically restore all mocked functionality once
413+ the test finishes.
414+
415+ ``` js
416+ test (' spies on an object method' , (t ) => {
417+ const number = {
418+ value: 5 ,
419+ add (a ) {
420+ return this .value + a;
421+ },
422+ };
423+
424+ t .mock .method (number, ' add' );
425+ assert .strictEqual (number .add .mock .calls .length , 0 );
426+ assert .strictEqual (number .add (3 ), 8 );
427+ assert .strictEqual (number .add .mock .calls .length , 1 );
428+
429+ const call = number .add .mock .calls [0 ];
430+
431+ assert .deepStrictEqual (call .arguments , [3 ]);
432+ assert .strictEqual (call .result , 8 );
433+ assert .strictEqual (call .target , undefined );
434+ assert .strictEqual (call .this , number);
435+ });
436+ ```
437+
355438## ` run([options]) `
356439
357440<!-- YAML
@@ -644,6 +727,281 @@ describe('tests', async () => {
644727});
645728```
646729
730+ ## Class: ` MockFunctionContext `
731+
732+ <!-- YAML
733+ added: REPLACEME
734+ -->
735+
736+ The ` MockFunctionContext ` class is used to inspect or manipulate the behavior of
737+ mocks created via the [ ` MockTracker ` ] [ ] APIs.
738+
739+ ### ` ctx.calls `
740+
741+ <!-- YAML
742+ added: REPLACEME
743+ -->
744+
745+ * {Array}
746+
747+ A getter that returns a copy of the internal array used to track calls to the
748+ mock. Each entry in the array is an object with the following properties.
749+
750+ * ` arguments ` {Array} An array of the arguments passed to the mock function.
751+ * ` error ` {any} If the mocked function threw then this property contains the
752+ thrown value. ** Default:** ` undefined ` .
753+ * ` result ` {any} The value returned by the mocked function.
754+ * ` stack ` {Error} An ` Error ` object whose stack can be used to determine the
755+ callsite of the mocked function invocation.
756+ * ` target ` {Function|undefined} If the mocked function is a constructor, this
757+ field contains the class being constructed. Otherwise this will be
758+ ` undefined ` .
759+ * ` this ` {any} The mocked function's ` this ` value.
760+
761+ ### ` ctx.callCount() `
762+
763+ <!-- YAML
764+ added: REPLACEME
765+ -->
766+
767+ * Returns: {integer} The number of times that this mock has been invoked.
768+
769+ This function returns the number of times that this mock has been invoked. This
770+ function is more efficient than checking ` ctx.calls.length ` because ` ctx.calls `
771+ is a getter that creates a copy of the internal call tracking array.
772+
773+ ### ` ctx.mockImplementation(implementation) `
774+
775+ <!-- YAML
776+ added: REPLACEME
777+ -->
778+
779+ * ` implementation ` {Function|AsyncFunction} The function to be used as the
780+ mock's new implementation.
781+
782+ This function is used to change the behavior of an existing mock.
783+
784+ The following example creates a mock function using ` t.mock.fn() ` , calls the
785+ mock function, and then changes the mock implementation to a different function.
786+
787+ ``` js
788+ test (' changes a mock behavior' , (t ) => {
789+ let cnt = 0 ;
790+
791+ function addOne () {
792+ cnt++ ;
793+ return cnt;
794+ }
795+
796+ function addTwo () {
797+ cnt += 2 ;
798+ return cnt;
799+ }
800+
801+ const fn = t .mock .fn (addOne);
802+
803+ assert .strictEqual (fn (), 1 );
804+ fn .mock .mockImplementation (addTwo);
805+ assert .strictEqual (fn (), 3 );
806+ assert .strictEqual (fn (), 5 );
807+ });
808+ ```
809+
810+ ### ` ctx.mockImplementationOnce(implementation[, onCall]) `
811+
812+ <!-- YAML
813+ added: REPLACEME
814+ -->
815+
816+ * ` implementation ` {Function|AsyncFunction} The function to be used as the
817+ mock's implementation for the invocation number specified by ` onCall ` .
818+ * ` onCall ` {integer} The invocation number that will use ` implementation ` . If
819+ the specified invocation has already occurred then an exception is thrown.
820+ ** Default:** The number of the next invocation.
821+
822+ This function is used to change the behavior of an existing mock for a single
823+ invocation. Once invocation ` onCall ` has occurred, the mock will revert to
824+ whatever behavior it would have used had ` mockImplementationOnce() ` not been
825+ called.
826+
827+ The following example creates a mock function using ` t.mock.fn() ` , calls the
828+ mock function, changes the mock implementation to a different function for the
829+ next invocation, and then resumes its previous behavior.
830+
831+ ``` js
832+ test (' changes a mock behavior once' , (t ) => {
833+ let cnt = 0 ;
834+
835+ function addOne () {
836+ cnt++ ;
837+ return cnt;
838+ }
839+
840+ function addTwo () {
841+ cnt += 2 ;
842+ return cnt;
843+ }
844+
845+ const fn = t .mock .fn (addOne);
846+
847+ assert .strictEqual (fn (), 1 );
848+ fn .mock .mockImplementationOnce (addTwo);
849+ assert .strictEqual (fn (), 3 );
850+ assert .strictEqual (fn (), 4 );
851+ });
852+ ```
853+
854+ ### ` ctx.restore() `
855+
856+ <!-- YAML
857+ added: REPLACEME
858+ -->
859+
860+ Resets the implementation of the mock function to its original behavior. The
861+ mock can still be used after calling this function.
862+
863+ ## Class: ` MockTracker `
864+
865+ <!-- YAML
866+ added: REPLACEME
867+ -->
868+
869+ The ` MockTracker ` class is used to manage mocking functionality. The test runner
870+ module provides a top level ` mock ` export which is a ` MockTracker ` instance.
871+ Each test also provides its own ` MockTracker ` instance via the test context's
872+ ` mock ` property.
873+
874+ ### ` mock.fn([original[, implementation]][, options]) `
875+
876+ <!-- YAML
877+ added: REPLACEME
878+ -->
879+
880+ * ` original ` {Function|AsyncFunction} An optional function to create a mock on.
881+ ** Default:** A no-op function.
882+ * ` implementation ` {Function|AsyncFunction} An optional function used as the
883+ mock implementation for ` original ` . This is useful for creating mocks that
884+ exhibit one behavior for a specified number of calls and then restore the
885+ behavior of ` original ` . ** Default:** The function specified by ` original ` .
886+ * ` options ` {Object} Optional configuration options for the mock function. The
887+ following properties are supported:
888+ * ` times ` {integer} The number of times that the mock will use the behavior of
889+ ` implementation ` . Once the mock function has been called ` times ` times, it
890+ will automatically restore the behavior of ` original ` . This value must be an
891+ integer greater than zero. ** Default:** ` Infinity ` .
892+ * Returns: {Proxy} The mocked function. The mocked function contains a special
893+ ` mock ` property, which is an instance of [ ` MockFunctionContext ` ] [ ] , and can
894+ be used for inspecting and changing the behavior of the mocked function.
895+
896+ This function is used to create a mock function.
897+
898+ The following example creates a mock function that increments a counter by one
899+ on each invocation. The ` times ` option is used to modify the mock behavior such
900+ that the first two invocations add two to the counter instead of one.
901+
902+ ``` js
903+ test (' mocks a counting function' , (t ) => {
904+ let cnt = 0 ;
905+
906+ function addOne () {
907+ cnt++ ;
908+ return cnt;
909+ }
910+
911+ function addTwo () {
912+ cnt += 2 ;
913+ return cnt;
914+ }
915+
916+ const fn = t .mock .fn (addOne, addTwo, { times: 2 });
917+
918+ assert .strictEqual (fn (), 2 );
919+ assert .strictEqual (fn (), 4 );
920+ assert .strictEqual (fn (), 5 );
921+ assert .strictEqual (fn (), 6 );
922+ });
923+ ```
924+
925+ ### ` mock.method(object, methodName[, implementation][, options]) `
926+
927+ <!-- YAML
928+ added: REPLACEME
929+ -->
930+
931+ * ` object ` {Object} The object whose method is being mocked.
932+ * ` methodName ` {string|symbol} The identifier of the method on ` object ` to mock.
933+ If ` object[methodName] ` is not a function, an error is thrown.
934+ * ` implementation ` {Function|AsyncFunction} An optional function used as the
935+ mock implementation for ` object[methodName] ` . ** Default:** The original method
936+ specified by ` object[methodName] ` .
937+ * ` options ` {Object} Optional configuration options for the mock method. The
938+ following properties are supported:
939+ * ` getter ` {boolean} If ` true ` , ` object[methodName] ` is treated as a getter.
940+ This option cannot be used with the ` setter ` option. ** Default:** false.
941+ * ` setter ` {boolean} If ` true ` , ` object[methodName] ` is treated as a setter.
942+ This option cannot be used with the ` getter ` option. ** Default:** false.
943+ * ` times ` {integer} The number of times that the mock will use the behavior of
944+ ` implementation ` . Once the mocked method has been called ` times ` times, it
945+ will automatically restore the original behavior. This value must be an
946+ integer greater than zero. ** Default:** ` Infinity ` .
947+ * Returns: {Proxy} The mocked method. The mocked method contains a special
948+ ` mock ` property, which is an instance of [ ` MockFunctionContext ` ] [ ] , and can
949+ be used for inspecting and changing the behavior of the mocked method.
950+
951+ This function is used to create a mock on an existing object method. The
952+ following example demonstrates how a mock is created on an existing object
953+ method.
954+
955+ ``` js
956+ test (' spies on an object method' , (t ) => {
957+ const number = {
958+ value: 5 ,
959+ subtract (a ) {
960+ return this .value - a;
961+ },
962+ };
963+
964+ t .mock .method (number, ' subtract' );
965+ assert .strictEqual (number .subtract .mock .calls .length , 0 );
966+ assert .strictEqual (number .subtract (3 ), 2 );
967+ assert .strictEqual (number .subtract .mock .calls .length , 1 );
968+
969+ const call = number .subtract .mock .calls [0 ];
970+
971+ assert .deepStrictEqual (call .arguments , [3 ]);
972+ assert .strictEqual (call .result , 2 );
973+ assert .strictEqual (call .error , undefined );
974+ assert .strictEqual (call .target , undefined );
975+ assert .strictEqual (call .this , number);
976+ });
977+ ```
978+
979+ ### ` mock.reset() `
980+
981+ <!-- YAML
982+ added: REPLACEME
983+ -->
984+
985+ This function restores the default behavior of all mocks that were previously
986+ created by this ` MockTracker ` and disassociates the mocks from the
987+ ` MockTracker ` instance. Once disassociated, the mocks can still be used, but the
988+ ` MockTracker ` instance can no longer be used to reset their behavior or
989+ otherwise interact with them.
990+
991+ After each test completes, this function is called on the test context's
992+ ` MockTracker ` . If the global ` MockTracker ` is used extensively, calling this
993+ function manually is recommended.
994+
995+ ### ` mock.restoreAll() `
996+
997+ <!-- YAML
998+ added: REPLACEME
999+ -->
1000+
1001+ This function restores the default behavior of all mocks that were previously
1002+ created by this ` MockTracker ` . Unlike ` mock.reset() ` , ` mock.restoreAll() ` does
1003+ not disassociate the mocks from the ` MockTracker ` instance.
1004+
6471005## Class: ` TapStream `
6481006
6491007<!-- YAML
@@ -979,6 +1337,8 @@ added:
9791337[ `--test-name-pattern` ] : cli.md#--test-name-pattern
9801338[ `--test-only` ] : cli.md#--test-only
9811339[ `--test` ] : cli.md#--test
1340+ [ `MockFunctionContext` ] : #class-mockfunctioncontext
1341+ [ `MockTracker` ] : #class-mocktracker
9821342[ `SuiteContext` ] : #class-suitecontext
9831343[ `TestContext` ] : #class-testcontext
9841344[ `context.diagnostic` ] : #contextdiagnosticmessage
0 commit comments