gpt4 book ai didi

typescript - 我应该如何测试 React Hook "useEffect"使用 Typescript 进行 api 调用?

转载 作者:搜寻专家 更新时间:2023-10-30 20:33:14 24 4
gpt4 key购买 nike

我正在使用 Typescript 和新的 React hooks 为一个简单的 React 应用编写一些有趣的 enzyme 测试。

但是,我似乎无法正确模拟在 useEffect Hook 中进行的 api 调用。

useEffect 调用 api 并使用“setData”更新 useState 状态“data”。

然后对象“数据”被映射到表格中对应的表格单元格。

这似乎应该很容易通过模拟 api 响应和 enzyme 安装来解决,但我不断收到错误提示我使用 act() 进行组件更新。

我尝试了很多使用 act() 的方法,但都无济于事。我尝试用 fetch 替换 axios 并使用 enzyme shallow 和 react-test-library 的渲染,但似乎没有任何效果。

组件:

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(...) 中。 我花了很多时间寻找解决方案,但找不到任何有效的方法,所以我决定鼓起勇气在这里问问。

最佳答案

这里有两个问题


异步调用setData

setDataPromise 回调中被调用。

一旦 Promise 解决,任何等待它的回调都会在 PromiseJobs queue 中排队。 . PromiseJobs 队列中的任何待处理作业都在当前消息完成后且下一个消息开始之前运行。

在这种情况下,当前运行的消息是您的测试,因此您的测试在 Promise 回调有机会运行之前完成,并且 setData 直到 才被调用在你的测试完成之后。

您可以通过使用诸如 setImmediate 之类的东西来延迟您的断言,直到 PromiseJobs 中的回调有机会运行之后才能解决此问题。

看起来您还需要调用 component.update() 以使用新状态重新呈现组件。 (我猜这是因为状态更改发生在 act 之外,因为没有任何方法可以将该回调代码包装在 act 中。)

总的来说,工作测试看起来像这样:

it('should render a proper table data', done => {
const mock = new MockAdapter(axios);
mock.onGet('/path/to/api').reply(200, response.data);
const component = mount(<SalesPlanTable />);
setImmediate(() => {
component.update();
console.log(component.debug());
done();
});
});

警告:对 ... 的更新未包含在 act(...) 中

警告是由发生在 act 之外的组件状态更新触发的。

useEffect 函数触发的setData 的异步调用 导致的状态更改将始终发生在 act 之外>.

这是一个非常简单的测试来演示这种行为:

import React, { useState, useEffect } from 'react';
import { mount } from 'enzyme';

const SimpleComponent = () => {
const [data, setData] = useState('initial');

useEffect(() => {
setImmediate(() => setData('updated'));
}, []);

return (<div>{data}</div>);
};

test('SimpleComponent', done => {
const wrapper = mount(<SimpleComponent/>);
setImmediate(done);
});

当我搜索更多信息时,我偶然发现了 enzyme issue #2073就在 10 小时前打开谈论同样的行为。

我添加了上面的测试in a comment帮助 enzyme 开发者解决这个问题。

关于typescript - 我应该如何测试 React Hook "useEffect"使用 Typescript 进行 api 调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55388587/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com