Reputation: 2425
How can I spy on publish
and publishBatch
inside an instance
property:
Object.defineProperty(Provider, 'instance', {
get: jest.fn(() => {
return {
publish: jest.fn(),
publishBatch: jest.fn()
}
}),
});
I'm aware of jest.spyOn(Provider, 'instance', 'get');
but I need to go deeper and couldn't find any information in the documentation.
Upvotes: 3
Views: 1343
Reputation: 1160
A bit late on the response, but since I just looked this up myself, I wanted to share a strategy that I found to work.
# <some-dir>/your-code.js
import { SomeClass } from "./some-class";
const newClass = new SomeClass();
export function testTarget() {
const deepMethodOne = newClass.classDeepMethodOne(1);
const deepMethodTwo = deepMethodOne.classDeepMethodTwo(2);
return true;
};
# <some-dir>/your-test.js
import { testTarget } from "./your-code";
jest.mock(("./some-class") => {
class MockSomeClass {
constructor() {}
classDeepMethodOne = (...args) => mockClassDeepMethodOne(...args);
}
return {
SomeClass: MockSomeClass
}
});
describe("Testing Your Code", () => {
const mockClassDeepMethodOne = jest.fn(() => ({
classDeepMethodTwo: mockClassDeepMethodTwo
}));
const classDeepMethodTwo = jest.fn();
it("spies on mocked deep method one", () => {
testTarget();
expect(mockClassDeepMethodOne).toHaveBeenCalledWith(1);
});
it("spies on mocked deep method two", () => {
testTarget();
expect(mockClassDeepMethodTwo).toHaveBeenCalledWith(2);
})
});
The notion behind why this works is that this gets around Jest's import-level hoisting restrictions by instantiating an immediate binding to data structures that would cause a delayed effect (e.g. functions).
A little more in depth: Jest hoists your import-level mocks prior to the imports, so it does not allow you to have any immediate bindings to any unhoisted code, e.g.:
# Player 1: Go to jail, do not pass go.
class MockSomeClass {
constructor() {}
classDeepMethodOne = (...args) => mockClassDeepMethodOne(...args);
}
jest.mock(("./some-class") => ({ SomeClass: MockSomeClass }));
# Player 2: Go to jail, do not pass go.
jest.mock(("./some-class") => {
class MockSomeClass {
constructor() {}
classDeepMethodOne = mockClassDeepMethodOne;
}
return {
SomeClass: MockSomeClass
}
});
Don't ask my why, I have no idea, probably a chicken and egg problem.
Upvotes: 0
Reputation: 2425
The solution is much easier than I thought:
const obj = {
publish: jest.fn(),
publishBatch: jest.fn()
}
Object.defineProperty(Provider, 'instance', {
get: jest.fn(() => {
return obj;
}),
});
const publishSpy = jest.spyOn(obj, 'publish');
...
expect(publishSpy).toHaveBeenCalled();
Upvotes: 2