Skip to content

Commit b942f93

Browse files
fossamagnaMoLow
authored andcommitted
feat: 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/node#45326 (comment) PR-URL: nodejs/node#45506 Reviewed-By: Moshe Atlow <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> (cherry picked from commit afed1afa55962211b6b56c2068d520b4d8a08888)
1 parent 5ba2500 commit b942f93

File tree

3 files changed

+162
-2
lines changed

3 files changed

+162
-2
lines changed

Diff for: README.md

+19
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,15 @@ test('mocks a counting function', (t) => {
866866
});
867867
```
868868

869+
### `mock.getter(object, methodName[, implementation][, options])`
870+
871+
<!-- YAML
872+
added: REPLACEME
873+
-->
874+
875+
This function is syntax sugar for [`MockTracker.method`][] with `options.getter`
876+
set to `true`.
877+
869878
### `mock.method(object, methodName[, implementation][, options])`
870879

871880
<!-- YAML
@@ -943,6 +952,15 @@ This function restores the default behavior of all mocks that were previously
943952
created by this `MockTracker`. Unlike `mock.reset()`, `mock.restoreAll()` does
944953
not disassociate the mocks from the `MockTracker` instance.
945954

955+
### `mock.setter(object, methodName[, implementation][, options])`
956+
957+
<!-- YAML
958+
added: REPLACEME
959+
-->
960+
961+
This function is syntax sugar for [`MockTracker.method`][] with `options.setter`
962+
set to `true`.
963+
946964
## Class: `TapStream`
947965

948966
<!-- YAML
@@ -1153,6 +1171,7 @@ The name of the suite.
11531171
[`AbortSignal`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
11541172
[TAP]: https://testanything.org/
11551173
[`MockFunctionContext`]: #class-mockfunctioncontext
1174+
[`MockTracker.method`]: #mockmethodobject-methodname-implementation-options
11561175
[`MockTracker`]: #class-mocktracke
11571176
[`SuiteContext`]: #class-suitecontext
11581177
[`TestContext`]: #class-testcontext

Diff for: lib/internal/test_runner/mock.js

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// https://github.com/nodejs/node/blob/7c6682957b3c5f86d0616cebc0ad09cc2a1fd50d/lib/internal/test_runner/mock.js
1+
// https://github.com/nodejs/node/blob/afed1afa55962211b6b56c2068d520b4d8a08888/lib/internal/test_runner/mock.js
22
'use strict'
33
const {
44
ArrayPrototypePush,
@@ -204,6 +204,60 @@ class MockTracker {
204204
return mock
205205
}
206206

207+
getter (
208+
object,
209+
methodName,
210+
implementation = kDefaultFunction,
211+
options = kEmptyObject
212+
) {
213+
if (implementation !== null && typeof implementation === 'object') {
214+
options = implementation
215+
implementation = kDefaultFunction
216+
} else {
217+
validateObject(options, 'options')
218+
}
219+
220+
const { getter = true } = options
221+
222+
if (getter === false) {
223+
throw new ERR_INVALID_ARG_VALUE(
224+
'options.getter', getter, 'cannot be false'
225+
)
226+
}
227+
228+
return this.method(object, methodName, implementation, {
229+
...options,
230+
getter
231+
})
232+
}
233+
234+
setter (
235+
object,
236+
methodName,
237+
implementation = kDefaultFunction,
238+
options = kEmptyObject
239+
) {
240+
if (implementation !== null && typeof implementation === 'object') {
241+
options = implementation
242+
implementation = kDefaultFunction
243+
} else {
244+
validateObject(options, 'options')
245+
}
246+
247+
const { setter = true } = options
248+
249+
if (setter === false) {
250+
throw new ERR_INVALID_ARG_VALUE(
251+
'options.setter', setter, 'cannot be false'
252+
)
253+
}
254+
255+
return this.method(object, methodName, implementation, {
256+
...options,
257+
setter
258+
})
259+
}
260+
207261
reset () {
208262
this.restoreAll()
209263
this.#mocks = []

Diff for: test/parallel/test-runner-mocking.js

+88-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// https://github.com/nodejs/node/blob/7c6682957b3c5f86d0616cebc0ad09cc2a1fd50d/test/parallel/test-runner-mocking.js
1+
// https://github.com/nodejs/node/blob/afed1afa55962211b6b56c2068d520b4d8a08888/test/parallel/test-runner-mocking.js
22
'use strict'
33
const common = require('../common')
44
const assert = require('node:assert')
@@ -535,6 +535,69 @@ test('mocks a setter', (t) => {
535535
assert.strictEqual(obj.prop, 65)
536536
})
537537

538+
test('mocks a getter with syntax sugar', (t) => {
539+
const obj = {
540+
prop: 5,
541+
get method () {
542+
return this.prop
543+
}
544+
}
545+
546+
function mockMethod () {
547+
return this.prop - 1
548+
}
549+
const getter = t.mock.getter(obj, 'method', mockMethod)
550+
assert.strictEqual(getter.mock.calls.length, 0)
551+
assert.strictEqual(obj.method, 4)
552+
553+
const call = getter.mock.calls[0]
554+
555+
assert.deepStrictEqual(call.arguments, [])
556+
assert.strictEqual(call.result, 4)
557+
assert.strictEqual(call.target, undefined)
558+
assert.strictEqual(call.this, obj)
559+
560+
assert.strictEqual(getter.mock.restore(), undefined)
561+
assert.strictEqual(obj.method, 5)
562+
})
563+
564+
test('mocks a setter with syntax sugar', (t) => {
565+
const obj = {
566+
prop: 100,
567+
// eslint-disable-next-line accessor-pairs
568+
set method (val) {
569+
this.prop = val
570+
}
571+
}
572+
573+
function mockMethod (val) {
574+
this.prop = -val
575+
}
576+
577+
assert.strictEqual(obj.prop, 100)
578+
obj.method = 88
579+
assert.strictEqual(obj.prop, 88)
580+
581+
const setter = t.mock.setter(obj, 'method', mockMethod)
582+
583+
assert.strictEqual(setter.mock.calls.length, 0)
584+
obj.method = 77
585+
assert.strictEqual(obj.prop, -77)
586+
assert.strictEqual(setter.mock.calls.length, 1)
587+
588+
const call = setter.mock.calls[0]
589+
590+
assert.deepStrictEqual(call.arguments, [77])
591+
assert.strictEqual(call.result, undefined)
592+
assert.strictEqual(call.target, undefined)
593+
assert.strictEqual(call.this, obj)
594+
595+
assert.strictEqual(setter.mock.restore(), undefined)
596+
assert.strictEqual(obj.prop, -77)
597+
obj.method = 65
598+
assert.strictEqual(obj.prop, 65)
599+
})
600+
538601
test('mocked functions match name and length', (t) => {
539602
function getNameAndLength (fn) {
540603
return {
@@ -800,3 +863,27 @@ test('spies on a class prototype method', (t) => {
800863
assert.strictEqual(call.target, undefined)
801864
assert.strictEqual(call.this, instance)
802865
})
866+
867+
test('getter() fails if getter options set to false', (t) => {
868+
assert.throws(() => {
869+
t.mock.getter({}, 'method', { getter: false })
870+
}, /The property 'options\.getter' cannot be false/)
871+
})
872+
873+
test('setter() fails if setter options set to false', (t) => {
874+
assert.throws(() => {
875+
t.mock.setter({}, 'method', { setter: false })
876+
}, /The property 'options\.setter' cannot be false/)
877+
})
878+
879+
test('getter() fails if setter options is true', (t) => {
880+
assert.throws(() => {
881+
t.mock.getter({}, 'method', { setter: true })
882+
}, /The property 'options\.setter' cannot be used with 'options\.getter'/)
883+
})
884+
885+
test('setter() fails if getter options is true', (t) => {
886+
assert.throws(() => {
887+
t.mock.setter({}, 'method', { getter: true })
888+
}, /The property 'options\.setter' cannot be used with 'options\.getter'/)
889+
})

0 commit comments

Comments
 (0)