我正在使用Jest + Testing-Library / React编写功能测试。经过几天的摸索,我发现当您使用.mockResolvedValue(...)
或.mockResolvedValueOnce(...)
时,模拟的范围不仅限于该测试...
import React from "react";
import { render, waitForElement } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import myApi from '../myApi';
jest.mock('../myApi'); // this will load __mocks__/myApi.js (see below)
import { wait } from '@testing-library/dom';
import App from "../components/App";
afterEach(() => {
jest.clearAllMocks();
});
describe("App", () => {
test("first test", async () => {
myApi.get.mockResolvedValueOnce('FOO');
// App will call myApi.get() once
const { container, getByText } = render(<App />);
await waitForElement(
() => getByText('FOO')
);
expect(myApi.get).toHaveBeenCalledTimes(1);
// This is going to "leak" into the next test
myApi.get.mockResolvedValueOnce('BAR');
});
test("second test", async () => {
// This is a decoy! The 'BAR' response in the previous test will be returned
myApi.get.mockResolvedValueOnce('FOO');
// App will call myApi.get() once (again)
const { container, getByText } = render(<App />);
// THIS WILL FAIL!
await waitForElement(
() => getByText('FOO')
);
expect(myApi.get).toHaveBeenCalledTimes(1);
});
});
__mocks__/myApi.js
如下所示:
export default {
get: jest.fn(() => Promise.resolve({ data: {} }))
};
我了解正在发生的事情:myApi
已导入两个测试的共享范围中。这就是.mockResolvedValue*
在测试之间应用的原因。
防止这种情况的正确方法是什么?测试应该是原子的,而不是相互耦合的。如果我在get
中触发了另一个first test
请求,则它应该无法中断second test
。太臭了!但是正确的模式是什么?我正在考虑将myApi
的不同“副本”克隆到本地测试范围中...但是我担心这会变得很奇怪并导致测试的信心下降。
我发现this question讨论了同一主题,但只说明了为什么会发生这种情况,而不是讨论避免这种情况的正确模式。
package.json
"dependencies": {
"axios": "^0.18.1",
"moment": "^2.24.0",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-redux": "^7.1.3",
"react-router-dom": "^5.1.2",
"react-scripts": "2.1.5",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^4.2.3",
"@testing-library/react": "^9.3.2",
"redux-mock-store": "^1.5.3",
"typescript": "^3.7.2"
}
答案 0 :(得分:2)
这是我构建测试的方式:
beforeAll
块
test
/ it
块中进行描述
--verbose
模式下失败的原因示例:
App.spec.js
describe("App", () => {
// jest allows nesting of test suits
// allowing us to have prettier reporting
// and having scoped variables
describe("Api.get returning FOO", () => {
// define variables used in the test suit
let wrapper;
// having the setup here
beforeAll(async () => {
Api.get.mockClear();
Api.get.mockResolvedValue("FOO");
const { container, getByText } = render(<App />);
// expose the container to the scope
wrapper = container;
await waitForElement(() => getByText("FOO"));
});
// write the test cases balow
// each assertion in a separate test block
test("should call the Api once", () => {
expect(Api.get).toHaveBeenCalledOnce();
});
test("should have been called with data", () => {
expect(Api.get).toHaveBeenCalledWith({ x: "y" });
});
test("should match the snapshot", () => {
expect(wrapper).toMatchSnapshot();
});
});
describe("Api.get returning BAR", () => {
// define variables used in the test suit
let wrapper;
beforeAll(async () => {
Api.get.mockClear();
Api.get.mockResolvedValue("BAR");
const { container, getByText } = render(<App />);
// expose the container to the scope
wrapper = container;
await waitForElement(() => getByText("FOO"));
});
test.todo("describe what is supposed to happen with Api.get");
test.todo("describe what is supposed to happen container");
});
});
回到问题-是的,将在整个测试文件中使用模拟功能,但是如果您尚未使用mockResolvedValueOnce
(泄漏到下一个测试中),则上述测试之一案例将失败,或者您的笔试成绩不佳。
编辑:
作为思想实验,您能想到可以“解决”该问题的结构吗?
要在每次测试后删除返回的模拟值和实现
afterEach(() => {
jest.resetAllMocks()
});