eme*_*his 7 integration-testing unit-testing jestjs react-testing-library
我正在使用 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);
});
});
Run Code Online (Sandbox Code Playgroud)
这是__mocks__/myApi.js看起来的样子:
export default {
get: jest.fn(() => Promise.resolve({ data: {} }))
};
Run Code Online (Sandbox Code Playgroud)
我明白发生了什么:myApi被导入到两个测试的共享范围中。这就是为什么.mockResolvedValue*适用于“跨”测试。
防止这种情况的正确方法是什么?测试应该是原子的,而不是相互耦合。如果我触发另一个get请求,first test它应该无法中断second test。好臭啊!但正确的模式是什么?我正在考虑将不同的“副本”克隆myApi到本地测试范围......但我担心这会变得奇怪并导致降低我测试的信心。
我发现这个问题讨论了相同的主题,但只解释了为什么会发生这种情况,而不是讨论避免它的正确模式。
包.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"
}
Run Code Online (Sandbox Code Playgroud)
这就是我构建测试的方式:
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");
});
});
Run Code Online (Sandbox Code Playgroud)
回到问题 - 是的,模拟函数将在整个测试文件中使用,但是如果您mockResolvedValueOnce没有使用它(泄漏到下一个测试中)上述测试用例之一将失败,或者您有一个很差的笔试。
编辑:
作为一个思想实验,你能想出一个可以“解决”这个问题的结构吗?
要在每次测试后删除返回模拟值和实现,您可以使用
afterEach(() => {
jest.resetAllMocks()
});
Run Code Online (Sandbox Code Playgroud)