@@ -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
@@ -630,6 +713,281 @@ describe('tests', async () => {
630
713
});
631
714
```
632
715
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
+
633
991
## Class: ` TapStream `
634
992
635
993
<!-- YAML
@@ -935,6 +1293,8 @@ added: v18.7.0
935
1293
[ `--test-name-pattern` ] : cli.md#--test-name-pattern
936
1294
[ `--test-only` ] : cli.md#--test-only
937
1295
[ `--test` ] : cli.md#--test
1296
+ [ `MockFunctionContext` ] : #class-mockfunctioncontext
1297
+ [ `MockTracker` ] : #class-mocktracker
938
1298
[ `SuiteContext` ] : #class-suitecontext
939
1299
[ `TestContext` ] : #class-testcontext
940
1300
[ `context.diagnostic` ] : #contextdiagnosticmessage
0 commit comments