Skip to content

Commit

Permalink
test_runner: ensure that mock.method works with class inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
ErickWendel committed Nov 30, 2022
1 parent 78d7141 commit 45b3bd4
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 9 deletions.
26 changes: 17 additions & 9 deletions lib/internal/test_runner/mock.js
Expand Up @@ -174,19 +174,27 @@ class MockTracker {

return original;
}
let descriptor = ObjectGetOwnPropertyDescriptor(object, methodName);
let original = getOriginalObject(descriptor);

// classes instances
if (typeof original !== 'function') {
descriptor = ObjectGetOwnPropertyDescriptor(
ObjectGetPrototypeOf(object),
methodName
);
function findMethodOnPrototypeChain(instance, method, isDescriptor = false) {
const original = isDescriptor ?
instance :
ObjectGetOwnPropertyDescriptor(instance, method);

if (original && !isDescriptor) {
return original;
}

original = getOriginalObject(descriptor);
const proto = ObjectGetPrototypeOf(instance);
const desc = ObjectGetOwnPropertyDescriptor(proto, method);
if (!desc && proto.name) {
return findMethodOnPrototypeChain(proto, method, true);
}
return desc;
}

const descriptor = findMethodOnPrototypeChain(object, methodName);
const original = getOriginalObject(descriptor);

if (typeof original !== 'function') {
throw new ERR_INVALID_ARG_VALUE(
'methodName', original, 'must be a method'
Expand Down
44 changes: 44 additions & 0 deletions test/parallel/test-runner-mocking.js
Expand Up @@ -318,6 +318,7 @@ test('spy functions can be bound', (t) => {
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) {
Expand Down Expand Up @@ -345,6 +346,13 @@ test('mocks prototype methods on an instance', async (t) => {
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);
Expand Down Expand Up @@ -381,6 +389,42 @@ test('spies on async static class methods', async (t) => {
assert.strictEqual(Runner.someTask.mock, undefined);
});

test('given null to a mock.method it throws a invalid argument error', (t) => {
assert.throws(() => t.mock.method(null, {}), /ERR_INVALID_ARG_TYPE/);
});

test('spy functions can be used on classes inheritance', (t) => {
class A {
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('mocked functions report thrown errors', (t) => {
const testError = new Error('test error');
const fn = t.mock.fn(() => {
Expand Down

0 comments on commit 45b3bd4

Please sign in to comment.