我要测试的是在点击Item
组件的元素之后,检查addToSelectList
是否被调用。
以下是简化代码。
ItemList组件
import React, {useState, useEffect} from 'react';
import {fetchItems} from '../api';
import {addToSelectList} from '../listModule';
import Item from './Item';
function ItemList() {
const [page, setPage] = useState(1);
const [items, setItems] = useState([]);
useEffect(() => {
fetchItems().then(res => {
setItems(res.data.items);
});
}, [page]);
function onClickItem(item) {
addToSelectList(item);
}
return (
<ul>
{items.map((item, index) => (
<li>
<Item key={index} name={item.name} onClick={() => {
onClickItem(item);
}} />
</li>
))}
</ul>
);
}
项目组件
import React from 'react';
function Item(props) {
return (
<div>
<div onClick={() => {
props.onClick();
}}>{props.name}</div>
</div>
);
}
itemList.test.js
import React from 'react';
import {mount} from 'enzyme';
import {addToSelectList} from '../listModule';
import ItemList from '../view/ItemList';
import Item from '../view/Item';
describe('<ItemList />, () => {
test('onClickItem', () => {
let wrapper = mount(<ItemList />);
// fail
// Expected: >0
// Received: 0
expect(wrapper.find(Item).length).toBeGreaterThan(0);
});
});
但是有两个问题。
当我mount
ItemList
组件并执行wrapper.find(Item)
时,没有Item
组件,因为Item
组件在{{ 1}}状态已更新。 如何检查那些items
组件是否正确呈现?
在我检查了Item
组件的呈现后,如何触发Item
组件的onClick
事件并检查Item
方法是叫?
答案 0 :(得分:1)
这是单元测试解决方案:
itemList.tsx
:
import React, { useState, useEffect } from 'react';
import { fetchItems } from './api';
import { addToSelectList } from './listModule';
import Item from './item';
export default function ItemList() {
const [page, setPage] = useState(1);
const [items, setItems] = useState([]);
useEffect(() => {
fetchItems().then((res) => {
setItems(res.data.items);
});
}, [page]);
function onClickItem(item) {
addToSelectList(item);
}
return (
<ul>
{items.map((item: any, index) => (
<li key={index}>
<Item
name={item.name}
onClick={() => {
onClickItem(item);
}}
/>
</li>
))}
</ul>
);
}
api.ts
:
export const fetchItems = async () => {
return { data: { items: [] } } as any;
};
item.tsx
:
import React from 'react';
export default function Item(props) {
return (
<div>
<div
onClick={() => {
props.onClick();
}}
>
{props.name}
</div>
</div>
);
}
listModule.ts
:
export const addToSelectList = (item) => {
console.log(item);
};
itemList.test.tsx
:
import React from 'react';
import ItemList from './itemList';
import { mount } from 'enzyme';
import { fetchItems } from './api';
import { act } from 'react-dom/test-utils';
import Item from './item';
import { addToSelectList } from './listModule';
jest.mock('./api', () => {
return { fetchItems: jest.fn() };
});
jest.mock('./listModule', () => {
return { addToSelectList: jest.fn() };
});
describe('59853199', () => {
it('should pass', async () => {
const mFetchItemsResponse = { data: { items: [{ name: 'a' }, { name: 'b' }] } };
(fetchItems as jest.MockedFunction<typeof fetchItems>).mockResolvedValueOnce(mFetchItemsResponse);
const wrapper = mount(<ItemList />);
expect(wrapper.find(Item).length).toBe(0);
// when useEffect stable
await act(async () => {
await new Promise((resolve) => setTimeout(resolve));
});
wrapper.update();
expect(wrapper.find(Item).length).toBeGreaterThan(0);
wrapper
.find(Item)
.at(0)
.prop('onClick')();
expect(addToSelectList).toBeCalledWith(mFetchItemsResponse.data.items[0]);
});
});
带有覆盖率报告的单元测试结果:
PASS src/stackoverflow/59853199/itemList.test.tsx
59853199
✓ should pass (98ms)
--------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
--------------|----------|----------|----------|----------|-------------------|
All files | 94.44 | 100 | 87.5 | 94.44 | |
item.tsx | 75 | 100 | 50 | 75 | 8 |
itemList.tsx | 100 | 100 | 100 | 100 | |
--------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.18s, estimated 10s
源代码:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59853199