test
Version:
Node.js 18's node:test, as an npm package
1,040 lines (832 loc) • 27.2 kB
JavaScript
// https://github.com/nodejs/node/blob/929aada39d0f418193ca03cc360ced8c5b4ce553/test/parallel/test-runner-mocking.js
const common = require('../common')
const assert = require('node:assert')
const { mock, test } = require('#node:test')
test('spies on a function', (t) => {
const sum = t.mock.fn((arg1, arg2) => {
return arg1 + arg2
})
assert.strictEqual(sum.mock.calls.length, 0)
assert.strictEqual(sum(3, 4), 7)
assert.strictEqual(sum.call(1000, 9, 1), 10)
assert.strictEqual(sum.mock.calls.length, 2)
let call = sum.mock.calls[0]
assert.deepStrictEqual(call.arguments, [3, 4])
assert.strictEqual(call.error, undefined)
assert.strictEqual(call.result, 7)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, undefined)
call = sum.mock.calls[1]
assert.deepStrictEqual(call.arguments, [9, 1])
assert.strictEqual(call.error, undefined)
assert.strictEqual(call.result, 10)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, 1000)
})
test('spies on a bound function', (t) => {
const bound = function (arg1, arg2) {
return this + arg1 + arg2
}.bind(50)
const sum = t.mock.fn(bound)
assert.strictEqual(sum.mock.calls.length, 0)
assert.strictEqual(sum(3, 4), 57)
assert.strictEqual(sum(9, 1), 60)
assert.strictEqual(sum.mock.calls.length, 2)
let call = sum.mock.calls[0]
assert.deepStrictEqual(call.arguments, [3, 4])
assert.strictEqual(call.result, 57)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, undefined)
call = sum.mock.calls[1]
assert.deepStrictEqual(call.arguments, [9, 1])
assert.strictEqual(call.result, 60)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, undefined)
})
test('spies on a constructor', (t) => {
class ParentClazz {
constructor (c) {
this.c = c
}
}
class Clazz extends ParentClazz {
#privateValue
constructor (a, b) {
super(a + b)
this.a = a
this.#privateValue = b
}
getPrivateValue () {
return this.#privateValue
}
}
const ctor = t.mock.fn(Clazz)
const instance = new ctor(42, 85) // eslint-disable-line new-cap
assert(instance instanceof Clazz)
assert(instance instanceof ParentClazz)
assert.strictEqual(instance.a, 42)
assert.strictEqual(instance.getPrivateValue(), 85)
assert.strictEqual(instance.c, 127)
assert.strictEqual(ctor.mock.calls.length, 1)
const call = ctor.mock.calls[0]
assert.deepStrictEqual(call.arguments, [42, 85])
assert.strictEqual(call.error, undefined)
assert.strictEqual(call.result, instance)
assert.strictEqual(call.target, Clazz)
assert.strictEqual(call.this, instance)
})
test('a no-op spy function is created by default', (t) => {
const fn = t.mock.fn()
assert.strictEqual(fn.mock.calls.length, 0)
assert.strictEqual(fn(3, 4), undefined)
assert.strictEqual(fn.mock.calls.length, 1)
const call = fn.mock.calls[0]
assert.deepStrictEqual(call.arguments, [3, 4])
assert.strictEqual(call.result, undefined)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, undefined)
})
test('internal no-op function can be reused', (t) => {
const fn1 = t.mock.fn()
fn1.prop = true
const fn2 = t.mock.fn()
fn1(1)
fn2(2)
fn1(3)
assert.notStrictEqual(fn1.mock, fn2.mock)
assert.strictEqual(fn1.mock.calls.length, 2)
assert.strictEqual(fn2.mock.calls.length, 1)
assert.strictEqual(fn1.prop, true)
assert.strictEqual(fn2.prop, undefined)
})
test('functions can be mocked multiple times at once', (t) => {
function sum (a, b) {
return a + b
}
function difference (a, b) {
return a - b
}
function product (a, b) {
return a * b
}
const fn1 = t.mock.fn(sum, difference)
const fn2 = t.mock.fn(sum, product)
assert.strictEqual(fn1(5, 3), 2)
assert.strictEqual(fn2(5, 3), 15)
assert.strictEqual(fn2(4, 2), 8)
assert(!('mock' in sum))
assert(!('mock' in difference))
assert(!('mock' in product))
assert.notStrictEqual(fn1.mock, fn2.mock)
assert.strictEqual(fn1.mock.calls.length, 1)
assert.strictEqual(fn2.mock.calls.length, 2)
})
test('internal no-op function can be reused as methods', (t) => {
const obj = {
_foo: 5,
_bar: 9,
foo () {
return this._foo
},
bar () {
return this._bar
}
}
t.mock.method(obj, 'foo')
obj.foo.prop = true
t.mock.method(obj, 'bar')
assert.strictEqual(obj.foo(), 5)
assert.strictEqual(obj.bar(), 9)
assert.strictEqual(obj.bar(), 9)
assert.notStrictEqual(obj.foo.mock, obj.bar.mock)
assert.strictEqual(obj.foo.mock.calls.length, 1)
assert.strictEqual(obj.bar.mock.calls.length, 2)
assert.strictEqual(obj.foo.prop, true)
assert.strictEqual(obj.bar.prop, undefined)
})
test('methods can be mocked multiple times but not at the same time', (t) => {
const obj = {
offset: 3,
sum (a, b) {
return this.offset + a + b
}
}
function difference (a, b) {
return this.offset + (a - b)
}
function product (a, b) {
return this.offset + a * b
}
const originalSum = obj.sum
const fn1 = t.mock.method(obj, 'sum', difference)
assert.strictEqual(obj.sum(5, 3), 5)
assert.strictEqual(obj.sum(5, 1), 7)
assert.strictEqual(obj.sum, fn1)
assert.notStrictEqual(fn1.mock, undefined)
assert.strictEqual(originalSum.mock, undefined)
assert.strictEqual(difference.mock, undefined)
assert.strictEqual(product.mock, undefined)
assert.strictEqual(fn1.mock.calls.length, 2)
const fn2 = t.mock.method(obj, 'sum', product)
assert.strictEqual(obj.sum(5, 3), 18)
assert.strictEqual(obj.sum, fn2)
assert.notStrictEqual(fn1, fn2)
assert.strictEqual(fn2.mock.calls.length, 1)
obj.sum.mock.restore()
assert.strictEqual(obj.sum, fn1)
obj.sum.mock.restore()
assert.strictEqual(obj.sum, originalSum)
assert.strictEqual(obj.sum.mock, undefined)
})
test('spies on an object method', (t) => {
const obj = {
prop: 5,
method (a, b) {
return a + b + this.prop
}
}
assert.strictEqual(obj.method(1, 3), 9)
t.mock.method(obj, 'method')
assert.strictEqual(obj.method.mock.calls.length, 0)
assert.strictEqual(obj.method(1, 3), 9)
const call = obj.method.mock.calls[0]
assert.deepStrictEqual(call.arguments, [1, 3])
assert.strictEqual(call.result, 9)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, obj)
assert.strictEqual(obj.method.mock.restore(), undefined)
assert.strictEqual(obj.method(1, 3), 9)
assert.strictEqual(obj.method.mock, undefined)
})
test('spies on a getter', (t) => {
const obj = {
prop: 5,
get method () {
return this.prop
}
}
assert.strictEqual(obj.method, 5)
const getter = t.mock.method(obj, 'method', { getter: true })
assert.strictEqual(getter.mock.calls.length, 0)
assert.strictEqual(obj.method, 5)
const call = getter.mock.calls[0]
assert.deepStrictEqual(call.arguments, [])
assert.strictEqual(call.result, 5)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, obj)
assert.strictEqual(getter.mock.restore(), undefined)
assert.strictEqual(obj.method, 5)
})
test('spies on a setter', (t) => {
const obj = {
prop: 100,
// eslint-disable-next-line accessor-pairs
set method (val) {
this.prop = val
}
}
assert.strictEqual(obj.prop, 100)
obj.method = 88
assert.strictEqual(obj.prop, 88)
const setter = t.mock.method(obj, 'method', { setter: true })
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('spy functions can be bound', (t) => {
const sum = t.mock.fn(function (arg1, arg2) {
return this + arg1 + arg2
})
const bound = sum.bind(1000)
assert.strictEqual(bound(9, 1), 1010)
assert.strictEqual(sum.mock.calls.length, 1)
const call = sum.mock.calls[0]
assert.deepStrictEqual(call.arguments, [9, 1])
assert.strictEqual(call.result, 1010)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, 1000)
assert.strictEqual(sum.mock.restore(), undefined)
assert.strictEqual(sum.bind(0)(2, 11), 13)
})
test('mocks prototype methods on an instance', async (t) => {
class Runner {
async someTask (msg) {
return Promise.resolve(msg)
}
async method (msg) {
await this.someTask(msg)
return msg
}
}
const msg = 'ok'
const obj = new Runner()
assert.strictEqual(await obj.method(msg), msg)
t.mock.method(obj, obj.someTask.name)
assert.strictEqual(obj.someTask.mock.calls.length, 0)
assert.strictEqual(await obj.method(msg), msg)
const call = obj.someTask.mock.calls[0]
assert.deepStrictEqual(call.arguments, [msg])
assert.strictEqual(await call.result, msg)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, obj)
const obj2 = new Runner()
// Ensure that a brand new instance is not mocked
assert.strictEqual(
obj2.someTask.mock,
undefined
)
assert.strictEqual(obj.someTask.mock.restore(), undefined)
assert.strictEqual(await obj.method(msg), msg)
assert.strictEqual(obj.someTask.mock, undefined)
assert.strictEqual(Runner.prototype.someTask.mock, undefined)
})
test('spies on async static class methods', async (t) => {
class Runner {
static async someTask (msg) {
return Promise.resolve(msg)
}
static async method (msg) {
await this.someTask(msg)
return msg
}
}
const msg = 'ok'
assert.strictEqual(await Runner.method(msg), msg)
t.mock.method(Runner, Runner.someTask.name)
assert.strictEqual(Runner.someTask.mock.calls.length, 0)
assert.strictEqual(await Runner.method(msg), msg)
const call = Runner.someTask.mock.calls[0]
assert.deepStrictEqual(call.arguments, [msg])
assert.strictEqual(await call.result, msg)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, Runner)
assert.strictEqual(Runner.someTask.mock.restore(), undefined)
assert.strictEqual(await Runner.method(msg), msg)
assert.strictEqual(Runner.someTask.mock, undefined)
assert.strictEqual(Runner.prototype.someTask, undefined)
})
test('given null to a mock.method it throws a invalid argument error', (t) => {
assert.throws(() => t.mock.method(null, {}), { code: 'ERR_INVALID_ARG_TYPE' })
})
test('it should throw given an inexistent property on a object instance', (t) => {
assert.throws(() => t.mock.method({ abc: 0 }, 'non-existent'), {
code: 'ERR_INVALID_ARG_VALUE'
})
})
test('spy functions can be used on classes inheritance', (t) => {
// Makes sure that having a null-prototype doesn't throw our system off
class A extends null {
static someTask (msg) {
return msg
}
static method (msg) {
return this.someTask(msg)
}
}
class B extends A {}
class C extends B {}
const msg = 'ok'
assert.strictEqual(C.method(msg), msg)
t.mock.method(C, C.someTask.name)
assert.strictEqual(C.someTask.mock.calls.length, 0)
assert.strictEqual(C.method(msg), msg)
const call = C.someTask.mock.calls[0]
assert.deepStrictEqual(call.arguments, [msg])
assert.strictEqual(call.result, msg)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, C)
assert.strictEqual(C.someTask.mock.restore(), undefined)
assert.strictEqual(C.method(msg), msg)
assert.strictEqual(C.someTask.mock, undefined)
})
test('spy functions don\'t affect the prototype chain', (t) => {
class A {
static someTask (msg) {
return msg
}
}
class B extends A {}
class C extends B {}
const msg = 'ok'
const ABeforeMockIsUnchanged = Object.getOwnPropertyDescriptor(A, A.someTask.name)
const BBeforeMockIsUnchanged = Object.getOwnPropertyDescriptor(B, B.someTask.name)
const CBeforeMockShouldNotHaveDesc = Object.getOwnPropertyDescriptor(C, C.someTask.name)
t.mock.method(C, C.someTask.name)
C.someTask(msg)
const BAfterMockIsUnchanged = Object.getOwnPropertyDescriptor(B, B.someTask.name)
const AAfterMockIsUnchanged = Object.getOwnPropertyDescriptor(A, A.someTask.name)
const CAfterMockHasDescriptor = Object.getOwnPropertyDescriptor(C, C.someTask.name)
assert.strictEqual(CBeforeMockShouldNotHaveDesc, undefined)
assert.ok(CAfterMockHasDescriptor)
assert.deepStrictEqual(ABeforeMockIsUnchanged, AAfterMockIsUnchanged)
assert.strictEqual(BBeforeMockIsUnchanged, BAfterMockIsUnchanged)
assert.strictEqual(BBeforeMockIsUnchanged, undefined)
assert.strictEqual(C.someTask.mock.restore(), undefined)
const CAfterRestoreKeepsDescriptor = Object.getOwnPropertyDescriptor(C, C.someTask.name)
assert.ok(CAfterRestoreKeepsDescriptor)
})
test('mocked functions report thrown errors', (t) => {
const testError = new Error('test error')
const fn = t.mock.fn(() => {
throw testError
})
assert.throws(fn, /test error/)
assert.strictEqual(fn.mock.calls.length, 1)
const call = fn.mock.calls[0]
assert.deepStrictEqual(call.arguments, [])
assert.strictEqual(call.error, testError)
assert.strictEqual(call.result, undefined)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, undefined)
})
test('mocked constructors report thrown errors', (t) => {
const testError = new Error('test error')
class Clazz {
constructor () {
throw testError
}
}
const ctor = t.mock.fn(Clazz)
assert.throws(() => {
new ctor() // eslint-disable-line new-cap, no-new
}, /test error/)
assert.strictEqual(ctor.mock.calls.length, 1)
const call = ctor.mock.calls[0]
assert.deepStrictEqual(call.arguments, [])
assert.strictEqual(call.error, testError)
assert.strictEqual(call.result, undefined)
assert.strictEqual(call.target, Clazz)
assert.strictEqual(call.this, undefined)
})
test('mocks a function', (t) => {
const sum = (arg1, arg2) => arg1 + arg2
const difference = (arg1, arg2) => arg1 - arg2
const fn = t.mock.fn(sum, difference)
assert.strictEqual(fn.mock.calls.length, 0)
assert.strictEqual(fn(3, 4), -1)
assert.strictEqual(fn(9, 1), 8)
assert.strictEqual(fn.mock.calls.length, 2)
let call = fn.mock.calls[0]
assert.deepStrictEqual(call.arguments, [3, 4])
assert.strictEqual(call.result, -1)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, undefined)
call = fn.mock.calls[1]
assert.deepStrictEqual(call.arguments, [9, 1])
assert.strictEqual(call.result, 8)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, undefined)
assert.strictEqual(fn.mock.restore(), undefined)
assert.strictEqual(fn(2, 11), 13)
})
test('mocks a constructor', (t) => {
class ParentClazz {
constructor (c) {
this.c = c
}
}
class Clazz extends ParentClazz {
#privateValue
constructor (a, b) {
super(a + b)
this.a = a
this.#privateValue = b
}
getPrivateValue () {
return this.#privateValue
}
}
class MockClazz {
#privateValue
constructor (z) {
this.z = z
}
}
const ctor = t.mock.fn(Clazz, MockClazz)
const instance = new ctor(42, 85) // eslint-disable-line new-cap
assert(!(instance instanceof MockClazz))
assert(instance instanceof Clazz)
assert(instance instanceof ParentClazz)
assert.strictEqual(instance.a, undefined)
assert.strictEqual(instance.c, undefined)
assert.strictEqual(instance.z, 42)
assert.strictEqual(ctor.mock.calls.length, 1)
const call = ctor.mock.calls[0]
assert.deepStrictEqual(call.arguments, [42, 85])
assert.strictEqual(call.result, instance)
assert.strictEqual(call.target, Clazz)
assert.strictEqual(call.this, instance)
assert.throws(() => {
instance.getPrivateValue()
}, /TypeError: Cannot read private member #privateValue /)
})
test('mocks an object method', (t) => {
const obj = {
prop: 5,
method (a, b) {
return a + b + this.prop
}
}
function mockMethod (a) {
return a + this.prop
}
assert.strictEqual(obj.method(1, 3), 9)
t.mock.method(obj, 'method', mockMethod)
assert.strictEqual(obj.method.mock.calls.length, 0)
assert.strictEqual(obj.method(1, 3), 6)
const call = obj.method.mock.calls[0]
assert.deepStrictEqual(call.arguments, [1, 3])
assert.strictEqual(call.result, 6)
assert.strictEqual(call.target, undefined)
assert.strictEqual(call.this, obj)
assert.strictEqual(obj.method.mock.restore(), undefined)
assert.strictEqual(obj.method(1, 3), 9)
assert.strictEqual(obj.method.mock, undefined)
})
test('mocks a getter', (t) => {
const obj = {
prop: 5,
get method () {
return this.prop
}
}
function mockMethod () {
return this.prop - 1
}
assert.strictEqual(obj.method, 5)
const getter = t.mock.method(obj, 'method', mockMethod, { getter: true })
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', (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.method(obj, 'method', mockMethod, { setter: true })
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('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 {
name: Object.getOwnPropertyDescriptor(fn, 'name'),
length: Object.getOwnPropertyDescriptor(fn, 'length')
}
}
function func1 () {}
const func2 = function (a) {} // eslint-disable-line func-style
const arrow = (a, b, c) => {}
const obj = { method (a, b) {} }
assert.deepStrictEqual(
getNameAndLength(func1),
getNameAndLength(t.mock.fn(func1))
)
assert.deepStrictEqual(
getNameAndLength(func2),
getNameAndLength(t.mock.fn(func2))
)
assert.deepStrictEqual(
getNameAndLength(arrow),
getNameAndLength(t.mock.fn(arrow))
)
assert.deepStrictEqual(
getNameAndLength(obj.method),
getNameAndLength(t.mock.method(obj, 'method', func1))
)
})
test('method() fails if method cannot be redefined', (t) => {
const obj = {
prop: 5
}
Object.defineProperty(obj, 'method', {
configurable: false,
value (a, b) {
return a + b + this.prop
}
})
function mockMethod (a) {
return a + this.prop
}
assert.throws(() => {
t.mock.method(obj, 'method', mockMethod)
}, /Cannot redefine property: method/)
assert.strictEqual(obj.method(1, 3), 9)
assert.strictEqual(obj.method.mock, undefined)
})
test('method() fails if field is a property instead of a method', (t) => {
const obj = {
prop: 5,
method: 100
}
function mockMethod (a) {
return a + this.prop
}
assert.throws(() => {
t.mock.method(obj, 'method', mockMethod)
}, /The argument 'methodName' must be a method/)
assert.strictEqual(obj.method, 100)
assert.strictEqual(obj.method.mock, undefined)
})
test('mocks can be auto-restored', (t) => {
let cnt = 0
function addOne () {
cnt++
return cnt
}
function addTwo () {
cnt += 2
return cnt
}
const fn = t.mock.fn(addOne, addTwo, { times: 2 })
assert.strictEqual(fn(), 2)
assert.strictEqual(fn(), 4)
assert.strictEqual(fn(), 5)
assert.strictEqual(fn(), 6)
})
test('mock implementation can be changed dynamically', (t) => {
let cnt = 0
function addOne () {
cnt++
return cnt
}
function addTwo () {
cnt += 2
return cnt
}
function addThree () {
cnt += 3
return cnt
}
const fn = t.mock.fn(addOne)
assert.strictEqual(fn.mock.callCount(), 0)
assert.strictEqual(fn(), 1)
assert.strictEqual(fn(), 2)
assert.strictEqual(fn(), 3)
assert.strictEqual(fn.mock.callCount(), 3)
fn.mock.mockImplementation(addTwo)
assert.strictEqual(fn(), 5)
assert.strictEqual(fn(), 7)
assert.strictEqual(fn.mock.callCount(), 5)
fn.mock.restore()
assert.strictEqual(fn(), 8)
assert.strictEqual(fn(), 9)
assert.strictEqual(fn.mock.callCount(), 7)
assert.throws(() => {
fn.mock.mockImplementationOnce(common.mustNotCall(), 6)
}, /Expected onCall to be >= 7/)
fn.mock.mockImplementationOnce(addThree, 7)
fn.mock.mockImplementationOnce(addTwo, 8)
assert.strictEqual(fn(), 12)
assert.strictEqual(fn(), 14)
assert.strictEqual(fn(), 15)
assert.strictEqual(fn.mock.callCount(), 10)
fn.mock.mockImplementationOnce(addThree)
assert.strictEqual(fn(), 18)
assert.strictEqual(fn(), 19)
assert.strictEqual(fn.mock.callCount(), 12)
})
test('local mocks are auto restored after the test finishes', async (t) => {
const obj = {
foo () {},
bar () {}
}
const originalFoo = obj.foo
const originalBar = obj.bar
assert.strictEqual(originalFoo, obj.foo)
assert.strictEqual(originalBar, obj.bar)
const mockFoo = t.mock.method(obj, 'foo')
assert.strictEqual(mockFoo, obj.foo)
assert.notStrictEqual(originalFoo, obj.foo)
assert.strictEqual(originalBar, obj.bar)
t.beforeEach(() => {
assert.strictEqual(mockFoo, obj.foo)
assert.strictEqual(originalBar, obj.bar)
})
t.afterEach(() => {
assert.strictEqual(mockFoo, obj.foo)
assert.notStrictEqual(originalBar, obj.bar)
})
await t.test('creates mocks that are auto restored', (t) => {
const mockBar = t.mock.method(obj, 'bar')
assert.strictEqual(mockFoo, obj.foo)
assert.strictEqual(mockBar, obj.bar)
assert.notStrictEqual(originalBar, obj.bar)
})
assert.strictEqual(mockFoo, obj.foo)
assert.strictEqual(originalBar, obj.bar)
})
test('uses top level mock', () => {
function sum (a, b) {
return a + b
}
function difference (a, b) {
return a - b
}
const fn = mock.fn(sum, difference)
assert.strictEqual(fn.mock.calls.length, 0)
assert.strictEqual(fn(3, 4), -1)
assert.strictEqual(fn.mock.calls.length, 1)
mock.reset()
assert.strictEqual(fn(3, 4), 7)
assert.strictEqual(fn.mock.calls.length, 2)
})
test('the getter and setter options cannot be used together', (t) => {
assert.throws(() => {
t.mock.method({}, 'method', { getter: true, setter: true })
}, /The property 'options\.setter' cannot be used with 'options\.getter'/)
})
test('method names must be strings or symbols', (t) => {
const symbol = Symbol('')
const obj = {
method () {},
[symbol] () {}
}
t.mock.method(obj, 'method')
t.mock.method(obj, symbol)
assert.throws(() => {
t.mock.method(obj, {})
}, /Expected methodName to be string,symbol/)
})
test('the times option must be an integer >= 1', (t) => {
assert.throws(() => {
t.mock.fn({ times: null })
}, /Expected options\.times to be number/)
assert.throws(() => {
t.mock.fn({ times: 0 })
}, /Expected options.times to be >= 1/)
assert.throws(() => {
t.mock.fn(() => {}, { times: 3.14159 })
}, /Expected options.times to be an integer/)
})
test('spies on a class prototype method', (t) => {
class Clazz {
constructor (c) {
this.c = c
}
getC () {
return this.c
}
}
const instance = new Clazz(85)
assert.strictEqual(instance.getC(), 85)
t.mock.method(Clazz.prototype, 'getC')
assert.strictEqual(instance.getC.mock.calls.length, 0)
assert.strictEqual(instance.getC(), 85)
assert.strictEqual(instance.getC.mock.calls.length, 1)
assert.strictEqual(Clazz.prototype.getC.mock.calls.length, 1)
const call = instance.getC.mock.calls[0]
assert.deepStrictEqual(call.arguments, [])
assert.strictEqual(call.result, 85)
assert.strictEqual(call.error, undefined)
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'/)
})