Bat*_*man 13 javascript reactjs jestjs enzyme
我正在使用 Jest 和 Enzyme 来测试 React 功能组件。
我的组件:
export const getGroups = async () => {
const data = await fetch(groupApiUrl);
return await data.json()
};
export default function MyWidget({
groupId,
}) {
// Store group object in state
const [group, setGroup] = useState(null);
// Retrive groups on load
useEffect(() => {
if (groupId && group === null) {
const runEffect = async () => {
const { groups } = await getGroups();
const groupData = groups.find(
g => g.name === groupId || g.id === Number(groupId)
);
setGroup(groupData);
};
runEffect();
}
}, [group, groupId]);
const params =
group && `&id=${group.id}&name=${group.name}`;
const src = `https://mylink.com?${params ? params : ''}`;
return (
<iframe src={src}></iframe>
);
}
Run Code Online (Sandbox Code Playgroud)
当我写这个测试时:
it('handles groupId and api call ', () => {
// the effect will get called
// the effect will call getGroups
// the iframe will contain group parameters for the given groupId
act(()=> {
const wrapper = shallow(<MyWidget surface={`${USAGE_SURFACES.metrics}`} groupId={1} />)
console.log(wrapper.find("iframe").prop('src'))
})
})
Run Code Online (Sandbox Code Playgroud)
返回的 src 不包含 url 中的组信息。我如何触发 useEffect 以及其中的所有内容?
编辑:我学到的一件事是shallow不会触发useEffect。我仍然没有得到正确的 src 但我已经切换到mount而不是shallow
这是一个最小的、完整的 mocking 示例fetch。您的组件几乎可以归结为通用的 fire-fetch-and-set-state-with-response-data 习惯用法:
import React, {useEffect, useState} from "react";
export default function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
(async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
setUsers(await res.json());
})();
}, []);
return <p>there are {users.length} users</p>;
};
Run Code Online (Sandbox Code Playgroud)
随意在浏览器中运行这个组件:
<script type="text/babel" defer>
const {useState, useEffect} = React;
const Users = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
(async () => {
const res = await fetch("https://jsonplaceholder.typicode.com/users");
setUsers(await res.json());
})();
}, []);
return <p>there are {users.length} users</p>;
};
ReactDOM.render(<Users />, document.body);
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>Run Code Online (Sandbox Code Playgroud)
您可以看到组件最初呈现的值为 0,然后当请求到达时,所有 10 个用户对象都处于状态,并触发第二次呈现以显示更新的文本。
让我们编写一个幼稚(但不正确)的单元测试,嘲笑fetch它,因为它在 Node 中不存在:
import {act} from "react-dom/test-utils";
import React from "react";
import Enzyme, {mount} from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import Users from "../src/Users";
Enzyme.configure({adapter: new Adapter()});
describe("Users", () => {
let wrapper;
let users;
beforeEach(() => {
const mockResponseData = [{id: 1}, {id: 2}, {id: 3}];
users = mockResponseData.map(e => ({...e}));
jest.clearAllMocks();
global.fetch = jest.fn(async () => ({
json: async () => mockResponseData
}));
wrapper = mount(<Users />);
});
it("renders a count of users", () => {
const p = wrapper.find("p");
expect(p.exists()).toBe(true);
expect(p.text()).toEqual("there are 3 users");
});
});
Run Code Online (Sandbox Code Playgroud)
一切看起来都很好——我们加载包装器,找到段落并检查文本。但是运行它会给出:
import {act} from "react-dom/test-utils";
import React from "react";
import Enzyme, {mount} from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import Users from "../src/Users";
Enzyme.configure({adapter: new Adapter()});
describe("Users", () => {
let wrapper;
let users;
beforeEach(() => {
const mockResponseData = [{id: 1}, {id: 2}, {id: 3}];
users = mockResponseData.map(e => ({...e}));
jest.clearAllMocks();
global.fetch = jest.fn(async () => ({
json: async () => mockResponseData
}));
wrapper = mount(<Users />);
});
it("renders a count of users", () => {
const p = wrapper.find("p");
expect(p.exists()).toBe(true);
expect(p.text()).toEqual("there are 3 users");
});
});
Run Code Online (Sandbox Code Playgroud)
显然,没有等待承诺并且包装器没有注册更改。当承诺在任务队列中等待时,断言在调用堆栈上同步运行。当承诺用数据解析时,套件已经结束。
我们想让测试块到await下一个滴答,也就是在运行之前等待调用堆栈和挂起的承诺解决。Node 提供setImmediate或process.nextTick用于实现此目的。
最后,该wrapper.update()函数启用与 React 组件树的同步,以便我们可以看到更新的 DOM。
这是最终的工作测试:
import {act} from "react-dom/test-utils";
import React from "react";
import Enzyme, {mount} from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import Users from "../src/Users";
Enzyme.configure({adapter: new Adapter()});
describe("Users", () => {
let wrapper;
let users;
beforeEach(() => {
const mockResponseData = [{id: 1}, {id: 2}, {id: 3}];
users = mockResponseData.map(e => ({...e}));
jest.clearAllMocks();
global.fetch = jest.fn(async () => ({
json: async () => mockResponseData
}));
wrapper = mount(<Users />);
});
it("renders a count of users", async () => {
// ^^^^^
await act(() => new Promise(setImmediate)); // <--
wrapper.update(); // <--
const p = wrapper.find("p");
expect(p.exists()).toBe(true);
expect(p.text()).toEqual("there are 3 users");
});
});
Run Code Online (Sandbox Code Playgroud)
该new Promise(setImmediate)技术还帮助我们在承诺解决之前对状态进行断言。act(from react-dom/test-utils) 是必要的,以避免Warning: An update to Users inside a test was not wrapped in act(...)弹出useEffect.
将这个测试添加到上面的代码中也通过了:
it("renders a count of 0 users initially", () => {
return act(() => {
const p = wrapper.find("p");
expect(p.exists()).toBe(true);
expect(p.text()).toEqual("there are 0 users");
return new Promise(setImmediate);
});
});
Run Code Online (Sandbox Code Playgroud)
使用 时测试回调是异步的setImmediate,因此必须返回一个 promise 以确保 Jest 正确等待它。
这篇文章使用了 Node 12、Jest 26.1.0、Enzyme 3.11.0 和 React 16.13.1。
你总是可以用玩笑来嘲笑。所以你需要的是:
jest.mock('React', () => ({
...jest.requireActual('React'),
useEffect: jest.fn(),
}));
Run Code Online (Sandbox Code Playgroud)
这允许仅模拟 useEffect 并保持其他实现实际。
import { useEffect } from 'react';
Run Code Online (Sandbox Code Playgroud)
useEffect.mock.calls[0](); // <<-- That will call implementation of your useEffect
Run Code Online (Sandbox Code Playgroud)
useEffect 已经被触发并正在工作,重点是它是一个async操作。所以需要等待fetch完成。您可以执行此操作的方法之一是: 1. 编写断言 2. 指定测试中的断言数量,以便 jest 知道它必须等待操作完成。
it('handles groupId and api call ', () => {
// the effect will get called
// the effect will call getGroups
// the iframe will contain group parameters for the given groupId
expect.assertions(1)
const wrapper = shallow(<UsageWidget surface={`${USAGE_SURFACES.metrics}`} groupId={2} />)
wrapper.update()
expect(whatever your expectation is)
});
Run Code Online (Sandbox Code Playgroud)
因为在这个例子中我只是写了断言,
expect.assertions(1)
Run Code Online (Sandbox Code Playgroud)
如果写多了,就需要改数字。
| 归档时间: |
|
| 查看次数: |
23498 次 |
| 最近记录: |