如何在useEffect(func,[])中使用异步api调用中的setState钩子测试组件
Posted
技术标签:
【中文标题】如何在useEffect(func,[])中使用异步api调用中的setState钩子测试组件【英文标题】:how to test component with setState hook inside async api call in useEffect(func, []) 【发布时间】:2021-11-07 16:53:45 【问题描述】:我在使用 react-native-testing-library 对组件进行单元测试时遇到问题。
我有一个这样的组件:
// components/TestComponent.js
function TestComponent()
const [data, setData] = useState();
useEffect(() =>
clientLibrary.getData()
.then((result) => setData(result.data); )
.catch((err) => //handle error here )
, []);
render (
<ListComponent
testID="comp"
data=data)
renderItem=(item) => <ListItem testID='item' data=item />
/>
);
我这样测试它:
// components/TestComponent.test.js
it('should render 10 list item', async () =>
const data = new Array(10).fill().map((v, idx) => (
id: `v_$idx`,
));
const req = jest.spyOn(clientLibrary, 'getData').mockImplementation(() =>
return Promise.resolve(data);
);
const queryByTestId, queryAllByTestId = render(
<TestComponent />,
);
expect(await queryByTestId('comp')).toBeTruthy(); // this will pass
expect(await queryAllByTestId('item').length).toEqual(10); // this will fail with result: 0 expected: 10
); // this failed
测试将失败/通过
Attempted to log "Warning: An update to TestComponent inside a test was not wrapped in act(...).
在 useEffect 中指向 setData
。
我尝试使用act()
包装渲染,使用act()
包装断言,不模拟api 调用,将整个测试包装在act()
中,但错误不会消失。
我已经尝试查看测试库文档/git/q&a 来解决这个案例,也搜索了 *** 问题,但我仍然无法让这个测试正常工作。
谁能指出正确的方向来解决这个问题?
注意:我不是要测试实现细节。我只想测试给定一个获取结果 X,该组件将按预期呈现,即呈现 10 个列表项。
【问题讨论】:
您应该等待并断言使用data
在您的ListComponent
中呈现的任何内容都存在 - 这将确保您的useEffect
中的逻辑已经运行。
感谢@juliomalves 的建议。我想我应该更正我的问题中的措辞。如果我检查某些东西,测试确实通过了,但它仍然抱怨 not wrapped in act
警告指向 useEffect 中的 setState,并且由于它以红色打印,我的大脑只是认为它失败了,因为这意味着我不是做正确的事,即使它通过了。如果测试通过,忽略警告是否安全?该警告并不能完全提高我对测试的信心......
这似乎是两个独立的问题:(1) 使用waitFor
或findBy
等待异步任务解决,(2) 处理act
警告。分别见how to test useEffect with act和React Native testing - act without await。
【参考方案1】:
您的组件在 useEffect
内挂载期间正在执行异步状态更新,因此渲染行为具有异步副作用,需要将其包装在 await act(async())
调用中。请参阅testing recipes documentation on data fetching。
你可以在你的测试中尝试这样的事情:
it('should render 10 list item', async () =>
// Get these from `screen` now instead of `render`
const queryByTestId, queryAllByTestId = screen
const data = new Array(10).fill().map((v, idx) => (
id: `v_$idx`,
));
const req = jest.spyOn(clientLibrary, 'getData').mockImplementation(() =>
return Promise.resolve(data);
);
await act(async () =>
render(
<TestComponent />
);
)
expect(await queryByTestId('comp')).toBeTruthy();
expect(await queryAllByTestId('item').length).toEqual(10);
);
【讨论】:
以上是关于如何在useEffect(func,[])中使用异步api调用中的setState钩子测试组件的主要内容,如果未能解决你的问题,请参考以下文章
如何在 useEffect 挂钩中使用 Promise.all 获取所有数据?