Skip to content

Commit 909b3d8

Browse files
fossamagnaErickWendel
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: nodejs#45326 (comment) PR-URL: nodejs#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 8171219 commit 909b3d8

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
@@ -941,6 +941,15 @@ test('mocks a counting function', (t) => {
941941
});
942942
```
943943

944+
### `mock.getter(object, methodName[, implementation][, options])`
945+
946+
<!-- YAML
947+
added: REPLACEME
948+
-->
949+
950+
This function is syntax sugar for [`MockTracker.method`][] with `options.getter`
951+
set to `true`.
952+
944953
### `mock.method(object, methodName[, implementation][, options])`
945954

946955
<!-- YAML
@@ -1021,6 +1030,15 @@ This function restores the default behavior of all mocks that were previously
10211030
created by this `MockTracker`. Unlike `mock.reset()`, `mock.restoreAll()` does
10221031
not disassociate the mocks from the `MockTracker` instance.
10231032

1033+
### `mock.setter(object, methodName[, implementation][, options])`
1034+
1035+
<!-- YAML
1036+
added: REPLACEME
1037+
-->
1038+
1039+
This function is syntax sugar for [`MockTracker.method`][] with `options.setter`
1040+
set to `true`.
1041+
10241042
## Class: `TapStream`
10251043

10261044
<!-- YAML
@@ -1356,6 +1374,7 @@ added:
13561374
[`--test-only`]: cli.md#--test-only
13571375
[`--test`]: cli.md#--test
13581376
[`MockFunctionContext`]: #class-mockfunctioncontext
1377+
[`MockTracker.method`]: #mockmethodobject-methodname-implementation-options
13591378
[`MockTracker`]: #class-mocktracker
13601379
[`SuiteContext`]: #class-suitecontext
13611380
[`TestContext`]: #class-testcontext

lib/internal/test_runner/mock.js

+54
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,60 @@ class MockTracker {
220220
return mock;
221221
}
222222

223+
getter(
224+
object,
225+
methodName,
226+
implementation = kDefaultFunction,
227+
options = kEmptyObject
228+
) {
229+
if (implementation !== null && typeof implementation === 'object') {
230+
options = implementation;
231+
implementation = kDefaultFunction;
232+
} else {
233+
validateObject(options, 'options');
234+
}
235+
236+
const { getter = true } = options;
237+
238+
if (getter === false) {
239+
throw new ERR_INVALID_ARG_VALUE(
240+
'options.getter', getter, 'cannot be false'
241+
);
242+
}
243+
244+
return this.method(object, methodName, implementation, {
245+
...options,
246+
getter,
247+
});
248+
}
249+
250+
setter(
251+
object,
252+
methodName,
253+
implementation = kDefaultFunction,
254+
options = kEmptyObject
255+
) {
256+
if (implementation !== null && typeof implementation === 'object') {
257+
options = implementation;
258+
implementation = kDefaultFunction;
259+
} else {
260+
validateObject(options, 'options');
261+
}
262+
263+
const { setter = true } = options;
264+
265+
if (setter === false) {
266+
throw new ERR_INVALID_ARG_VALUE(
267+
'options.setter', setter, 'cannot be false'
268+
);
269+
}
270+
271+
return this.method(object, methodName, implementation, {
272+
...options,
273+
setter,
274+
});
275+
}
276+
223277
reset() {
224278
this.restoreAll();
225279
this.#mocks = [];

test/parallel/test-runner-mocking.js

+87
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,69 @@ test('mocks a setter', (t) => {
596596
assert.strictEqual(obj.prop, 65);
597597
});
598598

599+
test('mocks a getter with syntax sugar', (t) => {
600+
const obj = {
601+
prop: 5,
602+
get method() {
603+
return this.prop;
604+
},
605+
};
606+
607+
function mockMethod() {
608+
return this.prop - 1;
609+
}
610+
const getter = t.mock.getter(obj, 'method', mockMethod);
611+
assert.strictEqual(getter.mock.calls.length, 0);
612+
assert.strictEqual(obj.method, 4);
613+
614+
const call = getter.mock.calls[0];
615+
616+
assert.deepStrictEqual(call.arguments, []);
617+
assert.strictEqual(call.result, 4);
618+
assert.strictEqual(call.target, undefined);
619+
assert.strictEqual(call.this, obj);
620+
621+
assert.strictEqual(getter.mock.restore(), undefined);
622+
assert.strictEqual(obj.method, 5);
623+
});
624+
625+
test('mocks a setter with syntax sugar', (t) => {
626+
const obj = {
627+
prop: 100,
628+
// eslint-disable-next-line accessor-pairs
629+
set method(val) {
630+
this.prop = val;
631+
},
632+
};
633+
634+
function mockMethod(val) {
635+
this.prop = -val;
636+
}
637+
638+
assert.strictEqual(obj.prop, 100);
639+
obj.method = 88;
640+
assert.strictEqual(obj.prop, 88);
641+
642+
const setter = t.mock.setter(obj, 'method', mockMethod);
643+
644+
assert.strictEqual(setter.mock.calls.length, 0);
645+
obj.method = 77;
646+
assert.strictEqual(obj.prop, -77);
647+
assert.strictEqual(setter.mock.calls.length, 1);
648+
649+
const call = setter.mock.calls[0];
650+
651+
assert.deepStrictEqual(call.arguments, [77]);
652+
assert.strictEqual(call.result, undefined);
653+
assert.strictEqual(call.target, undefined);
654+
assert.strictEqual(call.this, obj);
655+
656+
assert.strictEqual(setter.mock.restore(), undefined);
657+
assert.strictEqual(obj.prop, -77);
658+
obj.method = 65;
659+
assert.strictEqual(obj.prop, 65);
660+
});
661+
599662
test('mocked functions match name and length', (t) => {
600663
function getNameAndLength(fn) {
601664
return {
@@ -861,3 +924,27 @@ test('spies on a class prototype method', (t) => {
861924
assert.strictEqual(call.target, undefined);
862925
assert.strictEqual(call.this, instance);
863926
});
927+
928+
test('getter() fails if getter options set to false', (t) => {
929+
assert.throws(() => {
930+
t.mock.getter({}, 'method', { getter: false });
931+
}, /The property 'options\.getter' cannot be false/);
932+
});
933+
934+
test('setter() fails if setter options set to false', (t) => {
935+
assert.throws(() => {
936+
t.mock.setter({}, 'method', { setter: false });
937+
}, /The property 'options\.setter' cannot be false/);
938+
});
939+
940+
test('getter() fails if setter options is true', (t) => {
941+
assert.throws(() => {
942+
t.mock.getter({}, 'method', { setter: true });
943+
}, /The property 'options\.setter' cannot be used with 'options\.getter'/);
944+
});
945+
946+
test('setter() fails if getter options is true', (t) => {
947+
assert.throws(() => {
948+
t.mock.setter({}, 'method', { getter: true });
949+
}, /The property 'options\.setter' cannot be used with 'options\.getter'/);
950+
});

0 commit comments

Comments
 (0)