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