@@ -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
@@ -630,6 +713,281 @@ describe('tests', async () => {
630713});
631714```
632715
716+ ## Class: ` MockFunctionContext `
717+
718+ <!-- YAML
719+ added: REPLACEME
720+ -->
721+
722+ The ` MockFunctionContext ` class is used to inspect or manipulate the behavior of
723+ mocks created via the [ ` MockTracker ` ] [ ] APIs.
724+
725+ ### ` ctx.calls `
726+
727+ <!-- YAML
728+ added: REPLACEME
729+ -->
730+
731+ * {Array}
732+
733+ A getter that returns a copy of the internal array used to track calls to the
734+ mock. Each entry in the array is an object with the following properties.
735+
736+ * ` arguments ` {Array} An array of the arguments passed to the mock function.
737+ * ` error ` {any} If the mocked function threw then this property contains the
738+ thrown value. ** Default:** ` undefined ` .
739+ * ` result ` {any} The value returned by the mocked function.
740+ * ` stack ` {Error} An ` Error ` object whose stack can be used to determine the
741+ callsite of the mocked function invocation.
742+ * ` target ` {Function|undefined} If the mocked function is a constructor, this
743+ field contains the class being constructed. Otherwise this will be
744+ ` undefined ` .
745+ * ` this ` {any} The mocked function's ` this ` value.
746+
747+ ### ` ctx.callCount() `
748+
749+ <!-- YAML
750+ added: REPLACEME
751+ -->
752+
753+ * Returns: {integer} The number of times that this mock has been invoked.
754+
755+ This function returns the number of times that this mock has been invoked. This
756+ function is more efficient than checking ` ctx.calls.length ` because ` ctx.calls `
757+ is a getter that creates a copy of the internal call tracking array.
758+
759+ ### ` ctx.mockImplementation(implementation) `
760+
761+ <!-- YAML
762+ added: REPLACEME
763+ -->
764+
765+ * ` implementation ` {Function|AsyncFunction} The function to be used as the
766+ mock's new implementation.
767+
768+ This function is used to change the behavior of an existing mock.
769+
770+ The following example creates a mock function using ` t.mock.fn() ` , calls the
771+ mock function, and then changes the mock implementation to a different function.
772+
773+ ``` js
774+ test (' changes a mock behavior' , (t ) => {
775+ let cnt = 0 ;
776+
777+ function addOne () {
778+ cnt++ ;
779+ return cnt;
780+ }
781+
782+ function addTwo () {
783+ cnt += 2 ;
784+ return cnt;
785+ }
786+
787+ const fn = t .mock .fn (addOne);
788+
789+ assert .strictEqual (fn (), 1 );
790+ fn .mock .mockImplementation (addTwo);
791+ assert .strictEqual (fn (), 3 );
792+ assert .strictEqual (fn (), 5 );
793+ });
794+ ```
795+
796+ ### ` ctx.mockImplementationOnce(implementation[, onCall]) `
797+
798+ <!-- YAML
799+ added: REPLACEME
800+ -->
801+
802+ * ` implementation ` {Function|AsyncFunction} The function to be used as the
803+ mock's implementation for the invocation number specified by ` onCall ` .
804+ * ` onCall ` {integer} The invocation number that will use ` implementation ` . If
805+ the specified invocation has already occurred then an exception is thrown.
806+ ** Default:** The number of the next invocation.
807+
808+ This function is used to change the behavior of an existing mock for a single
809+ invocation. Once invocation ` onCall ` has occurred, the mock will revert to
810+ whatever behavior it would have used had ` mockImplementationOnce() ` not been
811+ called.
812+
813+ The following example creates a mock function using ` t.mock.fn() ` , calls the
814+ mock function, changes the mock implementation to a different function for the
815+ next invocation, and then resumes its previous behavior.
816+
817+ ``` js
818+ test (' changes a mock behavior once' , (t ) => {
819+ let cnt = 0 ;
820+
821+ function addOne () {
822+ cnt++ ;
823+ return cnt;
824+ }
825+
826+ function addTwo () {
827+ cnt += 2 ;
828+ return cnt;
829+ }
830+
831+ const fn = t .mock .fn (addOne);
832+
833+ assert .strictEqual (fn (), 1 );
834+ fn .mock .mockImplementationOnce (addTwo);
835+ assert .strictEqual (fn (), 3 );
836+ assert .strictEqual (fn (), 4 );
837+ });
838+ ```
839+
840+ ### ` ctx.restore() `
841+
842+ <!-- YAML
843+ added: REPLACEME
844+ -->
845+
846+ Resets the implementation of the mock function to its original behavior. The
847+ mock can still be used after calling this function.
848+
849+ ## Class: ` MockTracker `
850+
851+ <!-- YAML
852+ added: REPLACEME
853+ -->
854+
855+ The ` MockTracker ` class is used to manage mocking functionality. The test runner
856+ module provides a top level ` mock ` export which is a ` MockTracker ` instance.
857+ Each test also provides its own ` MockTracker ` instance via the test context's
858+ ` mock ` property.
859+
860+ ### ` mock.fn([original[, implementation]][, options]) `
861+
862+ <!-- YAML
863+ added: REPLACEME
864+ -->
865+
866+ * ` original ` {Function|AsyncFunction} An optional function to create a mock on.
867+ ** Default:** A no-op function.
868+ * ` implementation ` {Function|AsyncFunction} An optional function used as the
869+ mock implementation for ` original ` . This is useful for creating mocks that
870+ exhibit one behavior for a specified number of calls and then restore the
871+ behavior of ` original ` . ** Default:** The function specified by ` original ` .
872+ * ` options ` {Object} Optional configuration options for the mock function. The
873+ following properties are supported:
874+ * ` times ` {integer} The number of times that the mock will use the behavior of
875+ ` implementation ` . Once the mock function has been called ` times ` times, it
876+ will automatically restore the behavior of ` original ` . This value must be an
877+ integer greater than zero. ** Default:** ` Infinity ` .
878+ * Returns: {Proxy} The mocked function. The mocked function contains a special
879+ ` mock ` property, which is an instance of [ ` MockFunctionContext ` ] [ ] , and can
880+ be used for inspecting and changing the behavior of the mocked function.
881+
882+ This function is used to create a mock function.
883+
884+ The following example creates a mock function that increments a counter by one
885+ on each invocation. The ` times ` option is used to modify the mock behavior such
886+ that the first two invocations add two to the counter instead of one.
887+
888+ ``` js
889+ test (' mocks a counting function' , (t ) => {
890+ let cnt = 0 ;
891+
892+ function addOne () {
893+ cnt++ ;
894+ return cnt;
895+ }
896+
897+ function addTwo () {
898+ cnt += 2 ;
899+ return cnt;
900+ }
901+
902+ const fn = t .mock .fn (addOne, addTwo, { times: 2 });
903+
904+ assert .strictEqual (fn (), 2 );
905+ assert .strictEqual (fn (), 4 );
906+ assert .strictEqual (fn (), 5 );
907+ assert .strictEqual (fn (), 6 );
908+ });
909+ ```
910+
911+ ### ` mock.method(object, methodName[, implementation][, options]) `
912+
913+ <!-- YAML
914+ added: REPLACEME
915+ -->
916+
917+ * ` object ` {Object} The object whose method is being mocked.
918+ * ` methodName ` {string|symbol} The identifier of the method on ` object ` to mock.
919+ If ` object[methodName] ` is not a function, an error is thrown.
920+ * ` implementation ` {Function|AsyncFunction} An optional function used as the
921+ mock implementation for ` object[methodName] ` . ** Default:** The original method
922+ specified by ` object[methodName] ` .
923+ * ` options ` {Object} Optional configuration options for the mock method. The
924+ following properties are supported:
925+ * ` getter ` {boolean} If ` true ` , ` object[methodName] ` is treated as a getter.
926+ This option cannot be used with the ` setter ` option. ** Default:** false.
927+ * ` setter ` {boolean} If ` true ` , ` object[methodName] ` is treated as a setter.
928+ This option cannot be used with the ` getter ` option. ** Default:** false.
929+ * ` times ` {integer} The number of times that the mock will use the behavior of
930+ ` implementation ` . Once the mocked method has been called ` times ` times, it
931+ will automatically restore the original behavior. This value must be an
932+ integer greater than zero. ** Default:** ` Infinity ` .
933+ * Returns: {Proxy} The mocked method. The mocked method contains a special
934+ ` mock ` property, which is an instance of [ ` MockFunctionContext ` ] [ ] , and can
935+ be used for inspecting and changing the behavior of the mocked method.
936+
937+ This function is used to create a mock on an existing object method. The
938+ following example demonstrates how a mock is created on an existing object
939+ method.
940+
941+ ``` js
942+ test (' spies on an object method' , (t ) => {
943+ const number = {
944+ value: 5 ,
945+ subtract (a ) {
946+ return this .value - a;
947+ },
948+ };
949+
950+ t .mock .method (number, ' subtract' );
951+ assert .strictEqual (number .subtract .mock .calls .length , 0 );
952+ assert .strictEqual (number .subtract (3 ), 2 );
953+ assert .strictEqual (number .subtract .mock .calls .length , 1 );
954+
955+ const call = number .subtract .mock .calls [0 ];
956+
957+ assert .deepStrictEqual (call .arguments , [3 ]);
958+ assert .strictEqual (call .result , 2 );
959+ assert .strictEqual (call .error , undefined );
960+ assert .strictEqual (call .target , undefined );
961+ assert .strictEqual (call .this , number);
962+ });
963+ ```
964+
965+ ### ` mock.reset() `
966+
967+ <!-- YAML
968+ added: REPLACEME
969+ -->
970+
971+ This function restores the default behavior of all mocks that were previously
972+ created by this ` MockTracker ` and disassociates the mocks from the
973+ ` MockTracker ` instance. Once disassociated, the mocks can still be used, but the
974+ ` MockTracker ` instance can no longer be used to reset their behavior or
975+ otherwise interact with them.
976+
977+ After each test completes, this function is called on the test context's
978+ ` MockTracker ` . If the global ` MockTracker ` is used extensively, calling this
979+ function manually is recommended.
980+
981+ ### ` mock.restoreAll() `
982+
983+ <!-- YAML
984+ added: REPLACEME
985+ -->
986+
987+ This function restores the default behavior of all mocks that were previously
988+ created by this ` MockTracker ` . Unlike ` mock.reset() ` , ` mock.restoreAll() ` does
989+ not disassociate the mocks from the ` MockTracker ` instance.
990+
633991## Class: ` TapStream `
634992
635993<!-- YAML
@@ -935,6 +1293,8 @@ added: v18.7.0
9351293[ `--test-name-pattern` ] : cli.md#--test-name-pattern
9361294[ `--test-only` ] : cli.md#--test-only
9371295[ `--test` ] : cli.md#--test
1296+ [ `MockFunctionContext` ] : #class-mockfunctioncontext
1297+ [ `MockTracker` ] : #class-mocktracker
9381298[ `SuiteContext` ] : #class-suitecontext
9391299[ `TestContext` ] : #class-testcontext
9401300[ `context.diagnostic` ] : #contextdiagnosticmessage
0 commit comments