问题描述
jest.spyOn(obj, 'methodName') 会返回一个 mock.fn 对象,多次 spyOn 并不会更新这个对象,而是返回同一个对象,所以,会影响方法被调用的断言。
expect(obj.methodName).toBeCalled();
最小复原代码
- npm install --save-dev jest
- 添加下面的测试代码 xxx.test.js
- jest
可以看到 mock 打印出来的对象是同一个对象。有两次调用记录。
console.log
finally first = second: true
at Suite.<anonymous> (jjjest/spy.test.js:32:11)
console.log
{
calls: [ [ 'first' ] ],
instances: [ APIService {} ],
invocationCallOrder: [ 1 ],
results: [ { type: 'return', value: [Promise] } ]
}
at Object.<anonymous> (jjjest/spy.test.js:21:13)
console.log
{
calls: [ [ 'first' ], [ 'second' ] ],
instances: [ APIService {}, APIService {} ],
invocationCallOrder: [ 1, 2 ],
results: [
{ type: 'return', value: [Promise] },
{ type: 'return', value: [Promise] }
]
}
at Object.<anonymous> (jjjest/spy.test.js:30:13)
// https://jestjs.io/zh-Hans/docs/mock-functions
class APIService {
async getProduct(order) {
return Promise.resolve("hello");
}
}
async function getProduct(order) {
const api = new APIService();
await api.getProduct(order);
}
let first;
let second;
describe("spyOn will return the same Obj", () => {
it("first ", async () => {
first = jest.spyOn(APIService.prototype, "getProduct").mockResolvedValue("world");
await getProduct("first");
expect(APIService.prototype.getProduct).toBeCalled();
console.log(APIService.prototype.getProduct.mock);
});
it("second ", async () => {
second = jest.spyOn(APIService.prototype, "getProduct").mockResolvedValue("world");
await getProduct("second");
expect(APIService.prototype.getProduct).toBeCalled();
console.log(APIService.prototype.getProduct.mock);
});
console.log("finally first = second:", first === second);
});
解决方法
- 给 spyOn 的对象命名,在 beforeEach 进行 mockFn.mockClear
- 不要 mock prototype,针对每一个测试用例,生成单独的测试对象,这个比较冗余,可能不适合。
\
// https://jestjs.io/zh-Hans/docs/mock-functions
class APIService {
async getProduct(order) {
return Promise.resolve("hello");
}
}
async function getProduct(order) {
const api = new APIService();
await api.getProduct(order);
}
let mockApiGetProduct = jest.spyOn(APIService.prototype, "getProduct");
describe("spyOn will return the same Obj", () => {
beforeEach(() => {
mockApiGetProduct.mockClear();
});
it("first ", async () => {
mockApiGetProduct.mockResolvedValue("world");
await getProduct("first");
expect(mockApiGetProduct).toBeCalled();
console.log(mockApiGetProduct.mock);
});
it("second ", async () => {
mockApiGetProduct.mockResolvedValue("world-again");
await getProduct("second");
expect(mockApiGetProduct).toBeCalled();
console.log(mockApiGetProduct.mock);
});
});
console.log
{
calls: [ [ 'first' ] ],
instances: [ APIService {} ],
invocationCallOrder: [ 1 ],
results: [ { type: 'return', value: [Promise] } ]
}
at Object.<anonymous> (jjjest/restore.test.js:24:13)
console.log
{
calls: [ [ 'second' ] ],
instances: [ APIService {} ],
invocationCallOrder: [ 2 ],
results: [ { type: 'return', value: [Promise] } ]
}
at Object.<anonymous> (jjjest/restore.test.js:32:13)