@@ -352,6 +352,89 @@ Otherwise, the test is considered to be a failure. Test files must be
352
352
executable by Node.js, but are not required to use the ` node:test ` module
353
353
internally.
354
354
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
+
355
438
## ` run([options]) `
356
439
357
440
<!-- YAML
@@ -644,6 +727,281 @@ describe('tests', async () => {
644
727
});
645
728
```
646
729
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
+
647
1005
## Class: ` TapStream `
648
1006
649
1007
<!-- YAML
@@ -979,6 +1337,8 @@ added:
979
1337
[ `--test-name-pattern` ] : cli.md#--test-name-pattern
980
1338
[ `--test-only` ] : cli.md#--test-only
981
1339
[ `--test` ] : cli.md#--test
1340
+ [ `MockFunctionContext` ] : #class-mockfunctioncontext
1341
+ [ `MockTracker` ] : #class-mocktracker
982
1342
[ `SuiteContext` ] : #class-suitecontext
983
1343
[ `TestContext` ] : #class-testcontext
984
1344
[ `context.diagnostic` ] : #contextdiagnosticmessage
0 commit comments