GeezerVison is showing github.com — test_runner: add getter and setter to MockTracker · nodejs/node@a56560f
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/test.md +19Lines changed: 19 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -927,6 +927,15 @@ test('mocks a counting function', (t) => { | 927 | 927 | }); | 928 | 928 | ``` | 929 | 929 |
| | 930 | +### `mock.getter(object, methodName[, implementation][, options])` | | 931 | + | | 932 | +<!-- YAML | | 933 | +added: REPLACEME | | 934 | +--> | | 935 | + | | 936 | +This function is syntax sugar for [`MockTracker.method`][] with `options.getter` | | 937 | +set to `true`. | | 938 | + | 930 | 939 | ### `mock.method(object, methodName[, implementation][, options])` | 931 | 940 |
| 932 | 941 | <!-- YAML | @@ -1007,6 +1016,15 @@ This function restores the default behavior of all mocks that were previously | 1007 | 1016 | created by this `MockTracker`. Unlike `mock.reset()`, `mock.restoreAll()` does | 1008 | 1017 | not disassociate the mocks from the `MockTracker` instance. | 1009 | 1018 |
| | 1019 | +### `mock.setter(object, methodName[, implementation][, options])` | | 1020 | + | | 1021 | +<!-- YAML | | 1022 | +added: REPLACEME | | 1023 | +--> | | 1024 | + | | 1025 | +This function is syntax sugar for [`MockTracker.method`][] with `options.setter` | | 1026 | +set to `true`. | | 1027 | + | 1010 | 1028 | ## Class: `TapStream` | 1011 | 1029 |
| 1012 | 1030 | <!-- YAML | @@ -1312,6 +1330,7 @@ added: v18.7.0 | 1312 | 1330 | [`--test-only`]: cli.md#--test-only | 1313 | 1331 | [`--test`]: cli.md#--test | 1314 | 1332 | [`MockFunctionContext`]: #class-mockfunctioncontext | | 1333 | +[`MockTracker.method`]: #mockmethodobject-methodname-implementation-options | 1315 | 1334 | [`MockTracker`]: #class-mocktracker | 1316 | 1335 | [`SuiteContext`]: #class-suitecontext | 1317 | 1336 | [`TestContext`]: #class-testcontext | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/mock.js +54Lines changed: 54 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -202,6 +202,60 @@ class MockTracker { | 202 | 202 | return mock; | 203 | 203 | } | 204 | 204 |
| | 205 | + getter( | | 206 | + object, | | 207 | + methodName, | | 208 | + implementation = kDefaultFunction, | | 209 | + options = kEmptyObject | | 210 | + ) { | | 211 | + if (implementation !== null && typeof implementation === 'object') { | | 212 | + options = implementation; | | 213 | + implementation = kDefaultFunction; | | 214 | + } else { | | 215 | + validateObject(options, 'options'); | | 216 | + } | | 217 | + | | 218 | + const { getter = true } = options; | | 219 | + | | 220 | + if (getter === false) { | | 221 | + throw new ERR_INVALID_ARG_VALUE( | | 222 | + 'options.getter', getter, 'cannot be false' | | 223 | + ); | | 224 | + } | | 225 | + | | 226 | + return this.method(object, methodName, implementation, { | | 227 | + ...options, | | 228 | + getter, | | 229 | + }); | | 230 | + } | | 231 | + | | 232 | + setter( | | 233 | + object, | | 234 | + methodName, | | 235 | + implementation = kDefaultFunction, | | 236 | + options = kEmptyObject | | 237 | + ) { | | 238 | + if (implementation !== null && typeof implementation === 'object') { | | 239 | + options = implementation; | | 240 | + implementation = kDefaultFunction; | | 241 | + } else { | | 242 | + validateObject(options, 'options'); | | 243 | + } | | 244 | + | | 245 | + const { setter = true } = options; | | 246 | + | | 247 | + if (setter === false) { | | 248 | + throw new ERR_INVALID_ARG_VALUE( | | 249 | + 'options.setter', setter, 'cannot be false' | | 250 | + ); | | 251 | + } | | 252 | + | | 253 | + return this.method(object, methodName, implementation, { | | 254 | + ...options, | | 255 | + setter, | | 256 | + }); | | 257 | + } | | 258 | + | 205 | 259 | reset() { | 206 | 260 | this.restoreAll(); | 207 | 261 | this.#mocks = []; | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-runner-mocking.js +87Lines changed: 87 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -534,6 +534,69 @@ test('mocks a setter', (t) => { | 534 | 534 | assert.strictEqual(obj.prop, 65); | 535 | 535 | }); | 536 | 536 |
| | 537 | +test('mocks a getter with syntax sugar', (t) => { | | 538 | + const obj = { | | 539 | + prop: 5, | | 540 | + get method() { | | 541 | + return this.prop; | | 542 | + }, | | 543 | + }; | | 544 | + | | 545 | + function mockMethod() { | | 546 | + return this.prop - 1; | | 547 | + } | | 548 | + const getter = t.mock.getter(obj, 'method', mockMethod); | | 549 | + assert.strictEqual(getter.mock.calls.length, 0); | | 550 | + assert.strictEqual(obj.method, 4); | | 551 | + | | 552 | + const call = getter.mock.calls[0]; | | 553 | + | | 554 | + assert.deepStrictEqual(call.arguments, []); | | 555 | + assert.strictEqual(call.result, 4); | | 556 | + assert.strictEqual(call.target, undefined); | | 557 | + assert.strictEqual(call.this, obj); | | 558 | + | | 559 | + assert.strictEqual(getter.mock.restore(), undefined); | | 560 | + assert.strictEqual(obj.method, 5); | | 561 | +}); | | 562 | + | | 563 | +test('mocks a setter with syntax sugar', (t) => { | | 564 | + const obj = { | | 565 | + prop: 100, | | 566 | + // eslint-disable-next-line accessor-pairs | | 567 | + set method(val) { | | 568 | + this.prop = val; | | 569 | + }, | | 570 | + }; | | 571 | + | | 572 | + function mockMethod(val) { | | 573 | + this.prop = -val; | | 574 | + } | | 575 | + | | 576 | + assert.strictEqual(obj.prop, 100); | | 577 | + obj.method = 88; | | 578 | + assert.strictEqual(obj.prop, 88); | | 579 | + | | 580 | + const setter = t.mock.setter(obj, 'method', mockMethod); | | 581 | + | | 582 | + assert.strictEqual(setter.mock.calls.length, 0); | | 583 | + obj.method = 77; | | 584 | + assert.strictEqual(obj.prop, -77); | | 585 | + assert.strictEqual(setter.mock.calls.length, 1); | | 586 | + | | 587 | + const call = setter.mock.calls[0]; | | 588 | + | | 589 | + assert.deepStrictEqual(call.arguments, [77]); | | 590 | + assert.strictEqual(call.result, undefined); | | 591 | + assert.strictEqual(call.target, undefined); | | 592 | + assert.strictEqual(call.this, obj); | | 593 | + | | 594 | + assert.strictEqual(setter.mock.restore(), undefined); | | 595 | + assert.strictEqual(obj.prop, -77); | | 596 | + obj.method = 65; | | 597 | + assert.strictEqual(obj.prop, 65); | | 598 | +}); | | 599 | + | 537 | 600 | test('mocked functions match name and length', (t) => { | 538 | 601 | function getNameAndLength(fn) { | 539 | 602 | return { | @@ -799,3 +862,27 @@ test('spies on a class prototype method', (t) => { | 799 | 862 | assert.strictEqual(call.target, undefined); | 800 | 863 | assert.strictEqual(call.this, instance); | 801 | 864 | }); | | 865 | + | | 866 | +test('getter() fails if getter options set to false', (t) => { | | 867 | + assert.throws(() => { | | 868 | + t.mock.getter({}, 'method', { getter: false }); | | 869 | + }, /The property 'options\.getter' cannot be false/); | | 870 | +}); | | 871 | + | | 872 | +test('setter() fails if setter options set to false', (t) => { | | 873 | + assert.throws(() => { | | 874 | + t.mock.setter({}, 'method', { setter: false }); | | 875 | + }, /The property 'options\.setter' cannot be false/); | | 876 | +}); | | 877 | + | | 878 | +test('getter() fails if setter options is true', (t) => { | | 879 | + assert.throws(() => { | | 880 | + t.mock.getter({}, 'method', { setter: true }); | | 881 | + }, /The property 'options\.setter' cannot be used with 'options\.getter'/); | | 882 | +}); | | 883 | + | | 884 | +test('setter() fails if getter options is true', (t) => { | | 885 | + assert.throws(() => { | | 886 | + t.mock.setter({}, 'method', { getter: true }); | | 887 | + }, /The property 'options\.setter' cannot be used with 'options\.getter'/); | | 888 | +}); |
|