Skip to content

Commit 45b3bd4

Browse files
committed
test_runner: ensure that mock.method works with class inheritance
1 parent 78d7141 commit 45b3bd4

File tree

2 files changed

+61
-9
lines changed

2 files changed

+61
-9
lines changed

lib/internal/test_runner/mock.js

+17-9
Original file line numberDiff line numberDiff line change
@@ -174,19 +174,27 @@ class MockTracker {
174174

175175
return original;
176176
}
177-
let descriptor = ObjectGetOwnPropertyDescriptor(object, methodName);
178-
let original = getOriginalObject(descriptor);
179177

180-
// classes instances
181-
if (typeof original !== 'function') {
182-
descriptor = ObjectGetOwnPropertyDescriptor(
183-
ObjectGetPrototypeOf(object),
184-
methodName
185-
);
178+
function findMethodOnPrototypeChain(instance, method, isDescriptor = false) {
179+
const original = isDescriptor ?
180+
instance :
181+
ObjectGetOwnPropertyDescriptor(instance, method);
182+
183+
if (original && !isDescriptor) {
184+
return original;
185+
}
186186

187-
original = getOriginalObject(descriptor);
187+
const proto = ObjectGetPrototypeOf(instance);
188+
const desc = ObjectGetOwnPropertyDescriptor(proto, method);
189+
if (!desc && proto.name) {
190+
return findMethodOnPrototypeChain(proto, method, true);
191+
}
192+
return desc;
188193
}
189194

195+
const descriptor = findMethodOnPrototypeChain(object, methodName);
196+
const original = getOriginalObject(descriptor);
197+
190198
if (typeof original !== 'function') {
191199
throw new ERR_INVALID_ARG_VALUE(
192200
'methodName', original, 'must be a method'

test/parallel/test-runner-mocking.js

+44
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ test('spy functions can be bound', (t) => {
318318
assert.strictEqual(sum.mock.restore(), undefined);
319319
assert.strictEqual(sum.bind(0)(2, 11), 13);
320320
});
321+
321322
test('mocks prototype methods on an instance', async (t) => {
322323
class Runner {
323324
async someTask(msg) {
@@ -345,6 +346,13 @@ test('mocks prototype methods on an instance', async (t) => {
345346
assert.strictEqual(call.target, undefined);
346347
assert.strictEqual(call.this, obj);
347348

349+
const obj2 = new Runner();
350+
// Ensure that a brand new instance is not mocked
351+
assert.strictEqual(
352+
obj2.someTask.mock,
353+
undefined
354+
);
355+
348356
assert.strictEqual(obj.someTask.mock.restore(), undefined);
349357
assert.strictEqual(await obj.method(msg), msg);
350358
assert.strictEqual(obj.someTask.mock, undefined);
@@ -381,6 +389,42 @@ test('spies on async static class methods', async (t) => {
381389
assert.strictEqual(Runner.someTask.mock, undefined);
382390
});
383391

392+
test('given null to a mock.method it throws a invalid argument error', (t) => {
393+
assert.throws(() => t.mock.method(null, {}), /ERR_INVALID_ARG_TYPE/);
394+
});
395+
396+
test('spy functions can be used on classes inheritance', (t) => {
397+
class A {
398+
static someTask(msg) {
399+
return msg;
400+
}
401+
static method(msg) {
402+
return this.someTask(msg);
403+
}
404+
}
405+
class B extends A {}
406+
class C extends B {}
407+
408+
const msg = 'ok';
409+
assert.strictEqual(C.method(msg), msg);
410+
411+
t.mock.method(C, C.someTask.name);
412+
assert.strictEqual(C.someTask.mock.calls.length, 0);
413+
414+
assert.strictEqual(C.method(msg), msg);
415+
416+
const call = C.someTask.mock.calls[0];
417+
418+
assert.deepStrictEqual(call.arguments, [msg]);
419+
assert.strictEqual(call.result, msg);
420+
assert.strictEqual(call.target, undefined);
421+
assert.strictEqual(call.this, C);
422+
423+
assert.strictEqual(C.someTask.mock.restore(), undefined);
424+
assert.strictEqual(C.method(msg), msg);
425+
assert.strictEqual(C.someTask.mock, undefined);
426+
});
427+
384428
test('mocked functions report thrown errors', (t) => {
385429
const testError = new Error('test error');
386430
const fn = t.mock.fn(() => {

0 commit comments

Comments
 (0)