Skip to content

Commit

Permalink
test_runner: add getter and setter to MockTracker
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
fossamagna authored and targos committed Dec 12, 2022
1 parent e431612 commit efc4456
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 0 deletions.
19 changes: 19 additions & 0 deletions doc/api/test.md
Expand Up @@ -941,6 +941,15 @@ test('mocks a counting function', (t) => {
});
```

### `mock.getter(object, methodName[, implementation][, options])`

<!-- YAML
added: REPLACEME
-->

This function is syntax sugar for [`MockTracker.method`][] with `options.getter`
set to `true`.

### `mock.method(object, methodName[, implementation][, options])`

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

### `mock.setter(object, methodName[, implementation][, options])`

<!-- YAML
added: REPLACEME
-->

This function is syntax sugar for [`MockTracker.method`][] with `options.setter`
set to `true`.

## Class: `TapStream`

<!-- YAML
Expand Down Expand Up @@ -1356,6 +1374,7 @@ added:
[`--test-only`]: cli.md#--test-only
[`--test`]: cli.md#--test
[`MockFunctionContext`]: #class-mockfunctioncontext
[`MockTracker.method`]: #mockmethodobject-methodname-implementation-options
[`MockTracker`]: #class-mocktracker
[`SuiteContext`]: #class-suitecontext
[`TestContext`]: #class-testcontext
Expand Down
54 changes: 54 additions & 0 deletions lib/internal/test_runner/mock.js
Expand Up @@ -202,6 +202,60 @@ class MockTracker {
return mock;
}

getter(
object,
methodName,
implementation = kDefaultFunction,
options = kEmptyObject
) {
if (implementation !== null && typeof implementation === 'object') {
options = implementation;
implementation = kDefaultFunction;
} else {
validateObject(options, 'options');
}

const { getter = true } = options;

if (getter === false) {
throw new ERR_INVALID_ARG_VALUE(
'options.getter', getter, 'cannot be false'
);
}

return this.method(object, methodName, implementation, {
...options,
getter,
});
}

setter(
object,
methodName,
implementation = kDefaultFunction,
options = kEmptyObject
) {
if (implementation !== null && typeof implementation === 'object') {
options = implementation;
implementation = kDefaultFunction;
} else {
validateObject(options, 'options');
}

const { setter = true } = options;

if (setter === false) {
throw new ERR_INVALID_ARG_VALUE(
'options.setter', setter, 'cannot be false'
);
}

return this.method(object, methodName, implementation, {
...options,
setter,
});
}

reset() {
this.restoreAll();
this.#mocks = [];
Expand Down
87 changes: 87 additions & 0 deletions test/parallel/test-runner-mocking.js
Expand Up @@ -534,6 +534,69 @@ test('mocks a setter', (t) => {
assert.strictEqual(obj.prop, 65);
});

test('mocks a getter with syntax sugar', (t) => {
const obj = {
prop: 5,
get method() {
return this.prop;
},
};

function mockMethod() {
return this.prop - 1;
}
const getter = t.mock.getter(obj, 'method', mockMethod);
assert.strictEqual(getter.mock.calls.length, 0);
assert.strictEqual(obj.method, 4);

const call = getter.mock.calls[0];

assert.deepStrictEqual(call.arguments, []);
assert.strictEqual(call.result, 4);
assert.strictEqual(call.target, undefined);
assert.strictEqual(call.this, obj);

assert.strictEqual(getter.mock.restore(), undefined);
assert.strictEqual(obj.method, 5);
});

test('mocks a setter with syntax sugar', (t) => {
const obj = {
prop: 100,
// eslint-disable-next-line accessor-pairs
set method(val) {
this.prop = val;
},
};

function mockMethod(val) {
this.prop = -val;
}

assert.strictEqual(obj.prop, 100);
obj.method = 88;
assert.strictEqual(obj.prop, 88);

const setter = t.mock.setter(obj, 'method', mockMethod);

assert.strictEqual(setter.mock.calls.length, 0);
obj.method = 77;
assert.strictEqual(obj.prop, -77);
assert.strictEqual(setter.mock.calls.length, 1);

const call = setter.mock.calls[0];

assert.deepStrictEqual(call.arguments, [77]);
assert.strictEqual(call.result, undefined);
assert.strictEqual(call.target, undefined);
assert.strictEqual(call.this, obj);

assert.strictEqual(setter.mock.restore(), undefined);
assert.strictEqual(obj.prop, -77);
obj.method = 65;
assert.strictEqual(obj.prop, 65);
});

test('mocked functions match name and length', (t) => {
function getNameAndLength(fn) {
return {
Expand Down Expand Up @@ -799,3 +862,27 @@ test('spies on a class prototype method', (t) => {
assert.strictEqual(call.target, undefined);
assert.strictEqual(call.this, instance);
});

test('getter() fails if getter options set to false', (t) => {
assert.throws(() => {
t.mock.getter({}, 'method', { getter: false });
}, /The property 'options\.getter' cannot be false/);
});

test('setter() fails if setter options set to false', (t) => {
assert.throws(() => {
t.mock.setter({}, 'method', { setter: false });
}, /The property 'options\.setter' cannot be false/);
});

test('getter() fails if setter options is true', (t) => {
assert.throws(() => {
t.mock.getter({}, 'method', { setter: true });
}, /The property 'options\.setter' cannot be used with 'options\.getter'/);
});

test('setter() fails if getter options is true', (t) => {
assert.throws(() => {
t.mock.setter({}, 'method', { getter: true });
}, /The property 'options\.setter' cannot be used with 'options\.getter'/);
});

0 comments on commit efc4456

Please sign in to comment.