我正在使用Typescript和新的React hooks为一个简单的React应用程序编写一些jest-enzyme测试。
但是,我似乎无法正确模拟useEffect
钩子内部的API调用。useEffect
进行API调用并使用“setData”更新useState
状态“data”。
对象“data”然后被Map到表中到其对应的表单元格。
这看起来应该很容易用模拟的API响应和酶挂载来解决,但我不断收到错误,告诉我使用act()
进行组件更新。
我尝试了很多方法来使用act()
,但都没有用。我试过用fetch替换axios
,并使用enzyme shallow和react-test-library的render,但似乎都不起作用。
组件:
import axios from 'axios'
import React, { useEffect, useState } from 'react';
interface ISUB {
id: number;
mediaType: {
digital: boolean;
print: boolean;
};
monthlyPayment: {
digital: boolean;
print: boolean;
};
singleIssue: {
digital: boolean;
print: boolean;
};
subscription: {
digital: boolean;
print: boolean;
};
title: string;
}
interface IDATA extends Array<ISUB> {}
const initData: IDATA = [];
const SalesPlanTable = () => {
const [data, setData] = useState(initData);
useEffect(() => {
axios
.get(`/path/to/api`)
.then(res => {
setData(res.data.results);
})
.catch(error => console.log(error));
}, []);
const renderTableRows = () => {
return data.map((i: ISUB, k: number) => (
<tr key={k}>
<td>{i.id}</td>
<td>
{i.title}
</td>
<td>
{i.subscription.print}
{i.mediaType.digital}
</td>
<td>
{i.monthlyPayment.print}
{i.monthlyPayment.digital}
</td>
<td>
{i.singleIssue.print}
{i.singleIssue.digital}
</td>
<td>
<button>Submit</button>
</td>
</tr>
));
};
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>MediaType</th>
<th>MonthlyPayment</th>
<th>SingleIssue</th>
<th/>
</tr>
</thead>
<tbody'>{renderTableRows()}</tbody>
</table>
);
};
export default SalesPlanTable;
测试:
const response = {
data: {
results: [
{
id: 249,
mediaType: {
digital: true,
print: true
},
monthlyPayment: {
digital: true,
print: true
},
singleIssue: {
digital: true,
print: true
},
subscription: {
digital: true,
print: true
},
title: 'ELLE'
}
]
}
};
//after describe
it('should render a proper table data', () => {
const mock = new MockAdapter(axios);
mock.onGet('/path/to/api').reply(200, response.data);
act(() => {
component = mount(<SalesPlanTable />);
})
console.log(component.debug())
});
我希望它记录表的html,并呈现表体部分,我尝试了一些异步和不同的方法来模拟axios
,但我一直只得到表头或消息:在测试中对SalesPlanTable
的更新没有包含在act(...).
中,我找了很多小时的解决方案,但找不到任何有效的解决方案,所以我决定鼓起勇气在这里询问。
3条答案
按热度按时间kzmpq1sx1#
这里有两个问题
异步调用
setData
setData
在Promise
回调中被调用。一旦
Promise
解析,等待它的任何回调都会在PromiseJobs队列中排队。PromiseJobs队列中的任何挂起作业都将 * 在当前消息完成之后、下一个消息开始之前 * 运行。在这种情况下,当前正在运行的消息是您的测试,因此您的测试在
Promise
回调有机会运行之前完成,并且setData
直到在您的测试完成之后才被调用。可以通过使用
setImmediate
之类的东西来解决这个问题,将Assert延迟到PromiseJobs中的回调有机会运行之后。看起来您还需要调用
component.update()
来重新呈现具有新状态的组件。(我猜这是因为状态更改发生在act
之外,因为没有任何方法可以将回调代码 Package 在act
中。总的来说,工作测试看起来像这样:
警告:更新到...没有被包裹在行为(…)
该警告由
act
外部发生的组件状态更新触发。useEffect
函数触发的对setData
的异步调用导致的状态更改始终发生在act
之外。下面是一个非常简单的测试来演示这种行为:
当我在搜索更多信息时,我偶然发现了10个小时前刚刚打开的
enzyme
issue #2073,谈论了同样的行为。我在评论中添加了上述测试,以帮助
enzyme
开发人员解决这个问题。pftdvrlh2#
解决方案
它既能工作,又能摆脱
test was not wrapped in act(...)
警告。用途:
感谢...
这是edpark 11在issue@Brian_亚当斯的answer中提出的一个解决方案。
原文:https://github.com/enzymejs/enzyme/issues/2073#issuecomment-565736674
我把这篇文章复制到这里,为了存档起见做了一些修改。
whhtz7ly3#
通常,模仿用于发出获取请求的库是一种不好的做法。假设你想用fetch或isomorphic-unfetch替换axios?您必须用一个新的模拟来完全替换测试套件中的所有模拟。将测试绑定到服务器契约比绑定到模拟更好。
使用服务器存根库,如msw或nock + React Testing Library(RTL)。RTL有一些很棒的工具来在异步执行上踢React的生命周期。
下面是我将如何使用您提供的示例重写测试:
RTL + Nock
酶+ Nock