Description
🐛 Bug Report
When spying on an object or class, it is possible to mock a method that's defined higher up on the prototype chain. But when the original implementation is restored, the parent object's method is assigned to the child's.
To Reproduce
describe("Spying on protoypes", () => {
const A = { test: () => 1 };
const B = Object.create(A);
afterEach(() => {
jest.restoreAllMocks();
});
it("works with B", () => {
jest.spyOn(B, "test").mockReturnValue(3);
expect(B.test()).toEqual(3);
});
it("works with A", () => {
jest.spyOn(A, "test").mockReturnValue(2);
expect(B.test()).toEqual(2); // Fails, equals 1
});
});
The second test case above fails with the first test case is present, but passes if the first test case does not exist. If you console log / debugger this, you'll see that the first test is adding a new method to B's prototype, and restoreAllMocks
"restored" the mock from the first test by assigning A.test
to B.test
.
Expected behavior
Mock restoration should work regardless of whether the method is defined higher up on the prototype chain or not.
If B.hasOwnProperty('test')
is false, when jest.spyOn(B, 'test')
is restored, Jest should just delete B.test
rather than assign a value that might have existed only on B's prototype.
Link to repl or repo
Example above ☝️but here's a minimum setup with the latest version of Jest if needed: https://github.com/fongandrew/jest-proto-repo. Also includes a repro with an ES6 class inheritance example.
npx envinfo --preset jest
System:
OS: macOS 10.14.5
CPU: (12) x64 Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
Binaries:
Node: 10.1.0 - ~/.nvm/versions/node/v10.1.0/bin/node
npm: 6.0.1 - ~/.nvm/versions/node/v10.1.0/bin/npm
npmPackages:
jest: ^24.8.0 => 24.8.0