Skip to content

Commit a56560f

Browse files
fossamagnadanielleadams
authored andcommitted
test_runner: add getter and setter to MockTracker
This commit allows tests in test runner to use the `getter` and `setter` methods as "syntax sugar" for `MockTracker.method` with the `options.getter` or `options.setter` set to true in the options. Refs: #45326 (comment) PR-URL: #45506 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 53e01f6 commit a56560f

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed

doc/api/test.md

+19
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,15 @@ test('mocks a counting function', (t) => {
927927
});
928928
```
929929

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+
930939
### `mock.method(object, methodName[, implementation][, options])`
931940

932941
<!-- YAML
@@ -1007,6 +1016,15 @@ This function restores the default behavior of all mocks that were previously
10071016
created by this `MockTracker`. Unlike `mock.reset()`, `mock.restoreAll()` does
10081017
not disassociate the mocks from the `MockTracker` instance.
10091018

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+
10101028
## Class: `TapStream`
10111029

10121030
<!-- YAML
@@ -1312,6 +1330,7 @@ added: v18.7.0
13121330
[`--test-only`]: cli.md#--test-only
13131331
[`--test`]: cli.md#--test
13141332
[`MockFunctionContext`]: #class-mockfunctioncontext
1333+
[`MockTracker.method`]: #mockmethodobject-methodname-implementation-options
13151334
[`MockTracker`]: #class-mocktracker
13161335
[`SuiteContext`]: #class-suitecontext
13171336
[`TestContext`]: #class-testcontext

lib/internal/test_runner/mock.js

+54
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,60 @@ class MockTracker {
202202
return mock;
203203
}
204204

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+
205259
reset() {
206260
this.restoreAll();
207261
this.#mocks = [];

test/parallel/test-runner-mocking.js

+87
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,69 @@ test('mocks a setter', (t) => {
534534
assert.strictEqual(obj.prop, 65);
535535
});
536536

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+
537600
test('mocked functions match name and length', (t) => {
538601
function getNameAndLength(fn) {
539602
return {
@@ -799,3 +862,27 @@ test('spies on a class prototype method', (t) => {
799862
assert.strictEqual(call.target, undefined);
800863
assert.strictEqual(call.this, instance);
801864
});
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+
});

0 commit comments

Comments
 (0)