测试时,导致React状态更新的代码应包装为act(…)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了测试时,导致React状态更新的代码应包装为act(…)相关的知识,希望对你有一定的参考价值。
问题不是新问题,我已经在stackoverflow上看到了类似的问题,但是最多没有解决。即使通过测试,我也会收到此警告。试图通过将act(() => {});
包装在那些由于状态更改而导致的语句上来解决问题,但无法确定我应该在哪里使用它。任何帮助将不胜感激。我的错误消息:
● Console
console.error node_modules/react-dom/cjs/react-dom.development.js:88
Warning: An update to CompanyDetail inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser.
in CompanyDetail (at CompanyDetail.test.js:53)
in Router (created by BrowserRouter)
in BrowserRouter (at CompanyDetail.test.js:52)
console.error node_modules/react-dom/cjs/react-dom.development.js:88
Warning: An update to CompanyDetail inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser.
in CompanyDetail (at CompanyDetail.test.js:53)
in Router (created by BrowserRouter)
in BrowserRouter (at CompanyDetail.test.js:52)
CompanyDetails.js
的代码
import React from 'react';
import { Row } from 'react-bootstrap';
import CONSTANTS from '../../constants/constants';
import { Link } from 'react-router-dom';
import useFetch from '../../effects/use-fetch.effect';
import Spinner from '../spinner/Spinner';
const CompanyDetail = (props) => {
const { id } = props.match.params;
const [{ name, description, jobs, known_to_sponsor_visa }, isLoading] = useFetch(
`${CONSTANTS.BASE_URL}/companies/${id}`
);
let classes = 'badge badge-';
classes += known_to_sponsor_visa === 'Yes' ? 'success' : 'danger';
return (
<React.Fragment>
{!isLoading ? (
<Row style={{ margin: '20px' }}>
<div className="col-lg-6 col-md-6">
<div className="company-details">
<h1 data-testid="company-name">{name ? name : 'No name provided'}</h1>
<h5 data-testid="sponsors-visa">
Known to sponsor work visa: {''}
{known_to_sponsor_visa ? <span className={classes}> {known_to_sponsor_visa}</span> : 'No data'}
</h5>
<p data-testid="description">{description ? description : 'No description'}</p>
</div>
</div>
<div className="col-lg-6 col-md-6 ">
<div className="card-box ">
<div className="card-content content-primary">
<h3 className="card-title" data-testid="jobs">
{!jobs ? 'No jobs posted' : ` Last ${jobs.length} Jobs`}
</h3>
<ul>
{jobs
? jobs.map((job) => {
return (
<Link to={`/jobs/${job.job_id}`} data-testid="individual-job-link" key={job.job_id}>
<li data-testid="individual-job" key={job.job_id}>
{job.position}
</li>
</Link>
);
})
: 'No jobs posted'}
</ul>
</div>
</div>
</div>
</Row>
) : (
<div data-testid="spinner">
<Spinner />
</div>
)}
</React.Fragment>
);
};
export default CompanyDetail;
CompanyDetail.test.js
的代码
import React from 'react';
import CompanyDetail from '../../../components/company-detail/CompanyDetail';
import { BrowserRouter } from 'react-router-dom';
import { render, waitFor, fireEvent, act } from '@testing-library/react';
describe('Company Details', () => {
let mockData;
let data = {
name: 'Google',
description: 'this is a company description',
known_to_sponsor_visa: 'Yes',
id: '4',
jobs: [{ job_id: '2', position: 'Web developer' }],
};
beforeEach(() => {
jest.spyOn(global, 'fetch').mockResolvedValue({ json: jest.fn().mockResolvedValue({ data }) });
mockData = { match: { params: { id: 4 } } };
});
it('renders company details with given data', async () => {
const { getByTestId } = render(
<BrowserRouter>
<CompanyDetail {...mockData} key={data.jobs[0].job_id} />,
</BrowserRouter>
);
await waitFor(() => expect(getByTestId('company-name').textContent).toMatch('Google'));
await waitFor(() => expect(getByTestId('sponsors-visa').textContent).toMatch('Yes'));
await waitFor(() => expect(getByTestId('description').textContent).toMatch('this is a company description'));
});
it('renders correct jobs length', async () => {
const { getByTestId } = render(
<BrowserRouter>
<CompanyDetail {...mockData} key={data.jobs[0].job_id} />,
</BrowserRouter>
);
await waitFor(() => expect(getByTestId('jobs').textContent).toMatch('1'));
});
it('renders jobs', async () => {
const { getAllByTestId } = render(
<BrowserRouter>
<CompanyDetail {...mockData} key={data.jobs[0].job_id} />,
</BrowserRouter>
);
await waitFor(() => getAllByTestId('individual-job').map((li) => li.textContent));
});
it('renders spinner when there is no jobs', () => {
const { getByTestId } = render(
<BrowserRouter>
<CompanyDetail {...mockData} />,
</BrowserRouter>
);
expect(getByTestId('spinner').children.length).toBe(1);
});
it('navigates to individual job page', async () => {
const { getByText } = render(
<BrowserRouter>
<CompanyDetail {...mockData} />
</BrowserRouter>
);
await waitFor(() => fireEvent.click(getByText(data.jobs[0].position)));
await waitFor(() => expect(document.querySelector('a').getAttribute('href')).toBe(`/jobs/${data.jobs[0].job_id}`));
});
});
答案
实际上我解决了,这是我的小错误。我忘记将Spinner测试包装在async
和await
中。下面的代码解决了它。
it('renders spinner when there is no jobs', async () => {
const { getByTestId } = render(
<BrowserRouter>
<CompanyDetail {...mockData} />,
</BrowserRouter>
);
await waitFor(() => expect(getByTestId('spinner').children.length).toBe(1));
});
以上是关于测试时,导致React状态更新的代码应包装为act(…)的主要内容,如果未能解决你的问题,请参考以下文章
警告:测试中对 App 的更新未包含在酶和钩子中的 act(...) 中