@@ -359,6 +359,78 @@ Otherwise, the test is considered to be a failure. Test files must be
359
359
executable by Node.js, but are not required to use the ` node:test ` module
360
360
internally.
361
361
362
+ ## Mocking
363
+
364
+ The ` node:test ` module supports mocking during testing via a top-level ` mock `
365
+ object. The following example creates a spy on a function that adds two numbers
366
+ together. The spy is then used to assert that the function was called as
367
+ expected.
368
+
369
+ ``` mjs
370
+ import assert from ' node:assert' ;
371
+ import { mock , test } from ' node:test' ;
372
+ test (' spies on a function' , () => {
373
+ const sum = mock .fn ((a , b ) => {
374
+ return a + b;
375
+ });
376
+ assert .strictEqual (sum .mock .calls .length , 0 );
377
+ assert .strictEqual (sum (3 , 4 ), 7 );
378
+ assert .strictEqual (sum .mock .calls .length , 1 );
379
+ const call = sum .mock .calls [0 ];
380
+ assert .deepStrictEqual (call .arguments , [3 , 4 ]);
381
+ assert .strictEqual (call .result , 7 );
382
+ assert .strictEqual (call .error , undefined );
383
+ // Reset the globally tracked mocks.
384
+ mock .reset ();
385
+ });
386
+ ```
387
+
388
+ ``` cjs
389
+ ' use strict' ;
390
+ const assert = require (' node:assert' );
391
+ const { mock , test } = require (' node:test' );
392
+ test (' spies on a function' , () => {
393
+ const sum = mock .fn ((a , b ) => {
394
+ return a + b;
395
+ });
396
+ assert .strictEqual (sum .mock .calls .length , 0 );
397
+ assert .strictEqual (sum (3 , 4 ), 7 );
398
+ assert .strictEqual (sum .mock .calls .length , 1 );
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
+ // Reset the globally tracked mocks.
404
+ mock .reset ();
405
+ });
406
+ ```
407
+
408
+ The same mocking functionality is also exposed on the [ ` TestContext ` ] [ ] object
409
+ of each test. The following example creates a spy on an object method using the
410
+ API exposed on the ` TestContext ` . The benefit of mocking via the test context is
411
+ that the test runner will automatically restore all mocked functionality once
412
+ the test finishes.
413
+
414
+ ``` js
415
+ test (' spies on an object method' , (t ) => {
416
+ const number = {
417
+ value: 5 ,
418
+ add (a ) {
419
+ return this .value + a;
420
+ },
421
+ };
422
+ t .mock .method (number, ' add' );
423
+ assert .strictEqual (number .add .mock .calls .length , 0 );
424
+ assert .strictEqual (number .add (3 ), 8 );
425
+ assert .strictEqual (number .add .mock .calls .length , 1 );
426
+ const call = number .add .mock .calls [0 ];
427
+ assert .deepStrictEqual (call .arguments , [3 ]);
428
+ assert .strictEqual (call .result , 8 );
429
+ assert .strictEqual (call .target , undefined );
430
+ assert .strictEqual (call .this , number);
431
+ });
432
+ ```
433
+
362
434
## ` run([options]) `
363
435
364
436
<!-- YAML
@@ -611,6 +683,266 @@ describe('tests', async () => {
611
683
});
612
684
```
613
685
686
+ ## Class: ` MockFunctionContext `
687
+
688
+ <!-- YAML
689
+ added: REPLACEME
690
+ -->
691
+
692
+ The ` MockFunctionContext ` class is used to inspect or manipulate the behavior of
693
+ mocks created via the [ ` MockTracker ` ] [ ] APIs.
694
+
695
+ ### ` ctx.calls `
696
+
697
+ <!-- YAML
698
+ added: REPLACEME
699
+ -->
700
+
701
+ * {Array}
702
+
703
+ A getter that returns a copy of the internal array used to track calls to the
704
+ mock. Each entry in the array is an object with the following properties.
705
+
706
+ * ` arguments ` {Array} An array of the arguments passed to the mock function.
707
+ * ` error ` {any} If the mocked function threw then this property contains the
708
+ thrown value. ** Default:** ` undefined ` .
709
+ * ` result ` {any} The value returned by the mocked function.
710
+ * ` stack ` {Error} An ` Error ` object whose stack can be used to determine the
711
+ callsite of the mocked function invocation.
712
+ * ` target ` {Function|undefined} If the mocked function is a constructor, this
713
+ field contains the class being constructed. Otherwise this will be
714
+ ` undefined ` .
715
+ * ` this ` {any} The mocked function's ` this ` value.
716
+
717
+ ### ` ctx.callCount() `
718
+
719
+ <!-- YAML
720
+ added: REPLACEME
721
+ -->
722
+
723
+ * Returns: {integer} The number of times that this mock has been invoked.
724
+
725
+ This function returns the number of times that this mock has been invoked. This
726
+ function is more efficient than checking ` ctx.calls.length ` because ` ctx.calls `
727
+ is a getter that creates a copy of the internal call tracking array.
728
+
729
+ ### ` ctx.mockImplementation(implementation) `
730
+
731
+ <!-- YAML
732
+ added: REPLACEME
733
+ -->
734
+
735
+ * ` implementation ` {Function|AsyncFunction} The function to be used as the
736
+ mock's new implementation.
737
+
738
+ This function is used to change the behavior of an existing mock.
739
+
740
+ The following example creates a mock function using ` t.mock.fn() ` , calls the
741
+ mock function, and then changes the mock implementation to a different function.
742
+
743
+ ``` js
744
+ test (' changes a mock behavior' , (t ) => {
745
+ let cnt = 0 ;
746
+ function addOne () {
747
+ cnt++ ;
748
+ return cnt;
749
+ }
750
+ function addTwo () {
751
+ cnt += 2 ;
752
+ return cnt;
753
+ }
754
+ const fn = t .mock .fn (addOne);
755
+ assert .strictEqual (fn (), 1 );
756
+ fn .mock .mockImplementation (addTwo);
757
+ assert .strictEqual (fn (), 3 );
758
+ assert .strictEqual (fn (), 5 );
759
+ });
760
+ ```
761
+
762
+ ### ` ctx.mockImplementationOnce(implementation[, onCall]) `
763
+
764
+ <!-- YAML
765
+ added: REPLACEME
766
+ -->
767
+
768
+ * ` implementation ` {Function|AsyncFunction} The function to be used as the
769
+ mock's implementation for the invocation number specified by ` onCall ` .
770
+ * ` onCall ` {integer} The invocation number that will use ` implementation ` . If
771
+ the specified invocation has already occurred then an exception is thrown.
772
+ ** Default:** The number of the next invocation.
773
+
774
+ This function is used to change the behavior of an existing mock for a single
775
+ invocation. Once invocation ` onCall ` has occurred, the mock will revert to
776
+ whatever behavior it would have used had ` mockImplementationOnce() ` not been
777
+ called.
778
+
779
+ The following example creates a mock function using ` t.mock.fn() ` , calls the
780
+ mock function, changes the mock implementation to a different function for the
781
+ next invocation, and then resumes its previous behavior.
782
+
783
+ ``` js
784
+ test (' changes a mock behavior once' , (t ) => {
785
+ let cnt = 0 ;
786
+ function addOne () {
787
+ cnt++ ;
788
+ return cnt;
789
+ }
790
+ function addTwo () {
791
+ cnt += 2 ;
792
+ return cnt;
793
+ }
794
+ const fn = t .mock .fn (addOne);
795
+ assert .strictEqual (fn (), 1 );
796
+ fn .mock .mockImplementationOnce (addTwo);
797
+ assert .strictEqual (fn (), 3 );
798
+ assert .strictEqual (fn (), 4 );
799
+ });
800
+ ```
801
+
802
+ ### ` ctx.restore() `
803
+
804
+ <!-- YAML
805
+ added: REPLACEME
806
+ -->
807
+
808
+ Resets the implementation of the mock function to its original behavior. The
809
+ mock can still be used after calling this function.
810
+
811
+ ## Class: ` MockTracker `
812
+
813
+ <!-- YAML
814
+ added: REPLACEME
815
+ -->
816
+
817
+ The ` MockTracker ` class is used to manage mocking functionality. The test runner
818
+ module provides a top level ` mock ` export which is a ` MockTracker ` instance.
819
+ Each test also provides its own ` MockTracker ` instance via the test context's
820
+ ` mock ` property.
821
+
822
+ ### ` mock.fn([original[, implementation]][, options]) `
823
+
824
+ <!-- YAML
825
+ added: REPLACEME
826
+ -->
827
+
828
+ * ` original ` {Function|AsyncFunction} An optional function to create a mock on.
829
+ ** Default:** A no-op function.
830
+ * ` implementation ` {Function|AsyncFunction} An optional function used as the
831
+ mock implementation for ` original ` . This is useful for creating mocks that
832
+ exhibit one behavior for a specified number of calls and then restore the
833
+ behavior of ` original ` . ** Default:** The function specified by ` original ` .
834
+ * ` options ` {Object} Optional configuration options for the mock function. The
835
+ following properties are supported:
836
+ * ` times ` {integer} The number of times that the mock will use the behavior of
837
+ ` implementation ` . Once the mock function has been called ` times ` times, it
838
+ will automatically restore the behavior of ` original ` . This value must be an
839
+ integer greater than zero. ** Default:** ` Infinity ` .
840
+ * Returns: {Proxy} The mocked function. The mocked function contains a special
841
+ ` mock ` property, which is an instance of [ ` MockFunctionContext ` ] [ ] , and can
842
+ be used for inspecting and changing the behavior of the mocked function.
843
+
844
+ This function is used to create a mock function.
845
+
846
+ The following example creates a mock function that increments a counter by one
847
+ on each invocation. The ` times ` option is used to modify the mock behavior such
848
+ that the first two invocations add two to the counter instead of one.
849
+
850
+ ``` js
851
+ test (' mocks a counting function' , (t ) => {
852
+ let cnt = 0 ;
853
+ function addOne () {
854
+ cnt++ ;
855
+ return cnt;
856
+ }
857
+ function addTwo () {
858
+ cnt += 2 ;
859
+ return cnt;
860
+ }
861
+ const fn = t .mock .fn (addOne, addTwo, { times: 2 });
862
+ assert .strictEqual (fn (), 2 );
863
+ assert .strictEqual (fn (), 4 );
864
+ assert .strictEqual (fn (), 5 );
865
+ assert .strictEqual (fn (), 6 );
866
+ });
867
+ ```
868
+
869
+ ### ` mock.method(object, methodName[, implementation][, options]) `
870
+
871
+ <!-- YAML
872
+ added: REPLACEME
873
+ -->
874
+
875
+ * ` object ` {Object} The object whose method is being mocked.
876
+ * ` methodName ` {string|symbol} The identifier of the method on ` object ` to mock.
877
+ If ` object[methodName] ` is not a function, an error is thrown.
878
+ * ` implementation ` {Function|AsyncFunction} An optional function used as the
879
+ mock implementation for ` object[methodName] ` . ** Default:** The original method
880
+ specified by ` object[methodName] ` .
881
+ * ` options ` {Object} Optional configuration options for the mock method. The
882
+ following properties are supported:
883
+ * ` getter ` {boolean} If ` true ` , ` object[methodName] ` is treated as a getter.
884
+ This option cannot be used with the ` setter ` option. ** Default:** false.
885
+ * ` setter ` {boolean} If ` true ` , ` object[methodName] ` is treated as a setter.
886
+ This option cannot be used with the ` getter ` option. ** Default:** false.
887
+ * ` times ` {integer} The number of times that the mock will use the behavior of
888
+ ` implementation ` . Once the mocked method has been called ` times ` times, it
889
+ will automatically restore the original behavior. This value must be an
890
+ integer greater than zero. ** Default:** ` Infinity ` .
891
+ * Returns: {Proxy} The mocked method. The mocked method contains a special
892
+ ` mock ` property, which is an instance of [ ` MockFunctionContext ` ] [ ] , and can
893
+ be used for inspecting and changing the behavior of the mocked method.
894
+
895
+ This function is used to create a mock on an existing object method. The
896
+ following example demonstrates how a mock is created on an existing object
897
+ method.
898
+
899
+ ``` js
900
+ test (' spies on an object method' , (t ) => {
901
+ const number = {
902
+ value: 5 ,
903
+ subtract (a ) {
904
+ return this .value - a;
905
+ },
906
+ };
907
+ t .mock .method (number, ' subtract' );
908
+ assert .strictEqual (number .subtract .mock .calls .length , 0 );
909
+ assert .strictEqual (number .subtract (3 ), 2 );
910
+ assert .strictEqual (number .subtract .mock .calls .length , 1 );
911
+ const call = number .subtract .mock .calls [0 ];
912
+ assert .deepStrictEqual (call .arguments , [3 ]);
913
+ assert .strictEqual (call .result , 2 );
914
+ assert .strictEqual (call .error , undefined );
915
+ assert .strictEqual (call .target , undefined );
916
+ assert .strictEqual (call .this , number);
917
+ });
918
+ ```
919
+
920
+ ### ` mock.reset() `
921
+
922
+ <!-- YAML
923
+ added: REPLACEME
924
+ -->
925
+
926
+ This function restores the default behavior of all mocks that were previously
927
+ created by this ` MockTracker ` and disassociates the mocks from the
928
+ ` MockTracker ` instance. Once disassociated, the mocks can still be used, but the
929
+ ` MockTracker ` instance can no longer be used to reset their behavior or
930
+ otherwise interact with them.
931
+
932
+ After each test completes, this function is called on the test context's
933
+ ` MockTracker ` . If the global ` MockTracker ` is used extensively, calling this
934
+ function manually is recommended.
935
+
936
+ ### ` mock.restoreAll() `
937
+
938
+ <!-- YAML
939
+ added: REPLACEME
940
+ -->
941
+
942
+ This function restores the default behavior of all mocks that were previously
943
+ created by this ` MockTracker ` . Unlike ` mock.reset() ` , ` mock.restoreAll() ` does
944
+ not disassociate the mocks from the ` MockTracker ` instance.
945
+
614
946
## Class: ` TapStream `
615
947
616
948
<!-- YAML
@@ -821,6 +1153,8 @@ The name of the suite.
821
1153
822
1154
[ `AbortSignal` ] : https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
823
1155
[ TAP ] : https://testanything.org/
1156
+ [ `MockFunctionContext` ] : #class-mockfunctioncontext
1157
+ [ `MockTracker` ] : #class-mocktracke
824
1158
[ `SuiteContext` ] : #class-suitecontext
825
1159
[ `TestContext` ] : #class-testcontext
826
1160
[ `context.diagnostic` ] : #contextdiagnosticmessage
0 commit comments