我刚开始学习丹尼尔·欧文( Daniel )的“掌握反应测试驱动的开发”(Mastering React Driven Development),我认为将这些例子转换为React 18应该不难,但我在用Jest转换书中的第一个测试时遇到了麻烦。
这本书不使用create-react-app
或任何东西,而是从头开始构建React应用程序,所以我很难找到如何转换代码的相关示例。
如书中所写,在“反应17”中,测试通过了。但是如果我用ReactDOM.render()
替换createRoot()
,测试就失败了。
我的应用程序目录如下:
├── package.json
├── package-lock.json
├── src
│ └── Appointment.js
└── test
└── Appointment.test.js
文件内容如下:
package.json
{
"name": "appointments",
"version": "1.0.0",
"description": "Appointments project from Mastering React Test-Driven Development.",
"main": "index.js",
"scripts": {
"test": "jest"
},
"repository": {
"type": "git",
"url": "example.com"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/plugin-transform-runtime": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/preset-react": "^7.18.6",
"jest": "^28.1.2",
"jest-environment-jsdom": "^28.1.3"
},
"dependencies": {
"@babel/runtime": "^7.18.6",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"jest": {
"testEnvironment": "jsdom"
}
}
src/Appointment.js
import React from 'react';
export const Appointment = () => <div>Ashley</div>;
test/Appointment.test.js
import React from 'react';
import ReactDOM from 'react-dom';
// import {createRoot} from 'react-dom/client';
import {Appointment} from '../src/Appointment';
describe('Appointment', () => {
it("renders the customer's first name.", () => {
const customer = {firstName: 'Ashley'};
const component = <Appointment customer={customer} />;
const container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(component, container);
// const root = createRoot(container);
// root.render(component);
expect(document.body.textContent).toMatch('Ashley');
});
});
使用ReactDOM.render()
,测试通过,但我得到以下错误:
console.error
Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot
11 | document.body.appendChild(container);
12 |
> 13 | ReactDOM.render(component, container);
| ^
14 |
15 | expect(document.body.textContent).toMatch('Ashley');
16 | });
at printWarning (node_modules/react-dom/cjs/react-dom.development.js:86:30)
at error (node_modules/react-dom/cjs/react-dom.development.js:60:7)
at Object.render (node_modules/react-dom/cjs/react-dom.development.js:29670:5)
at Object.render (test/Appointment.test.js:13:14)
我查看了如何将ReactDOM.render()
转换为createRoot()
,并将测试更改为:
import React from 'react';
// import ReactDOM from 'react-dom';
import {createRoot} from 'react-dom/client';
import {Appointment} from '../src/Appointment';
describe('Appointment', () => {
it("renders the customer's first name.", () => {
const customer = {firstName: 'Ashley'};
const component = <Appointment customer={customer} />;
const container = document.createElement('div');
document.body.appendChild(container);
// ReactDOM.render(component, container);
const root = createRoot(container);
root.render(component);
expect(document.body.textContent).toMatch('Ashley');
});
});
测试失败如下:
> appointments@1.0.0 test
> jest
FAIL test/Appointment.test.js
Appointment
✕ renders the customer's first name. (9 ms)
● Appointment › renders the customer's first name.
expect(received).toMatch(expected)
Expected substring: "Ashley"
Received string: ""
17 | root.render(component);
18 |
> 19 | expect(document.body.textContent).toMatch('Ashley');
| ^
20 | });
21 | });
22 |
at Object.toMatch (test/Appointment.test.js:19:39)
at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:317:13)
at runJest (node_modules/@jest/core/build/runJest.js:407:19)
at _run10000 (node_modules/@jest/core/build/cli/index.js:339:7)
at runCLI (node_modules/@jest/core/build/cli/index.js:190:3)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.979 s, estimated 1 s
Ran all test suites.
如何使用createRoot()
通过这个测试?
发布于 2022-07-17 18:23:39
在进一步挖掘之后,我发现act()
可以在React 18中使用,在检查测试断言之前强制呈现。这允许测试在测试失败时立即运行,而无需等待Jest的done()超时。
在Jest配置中,globals.IS_REACT_ACT_ENVIRONMENT
必须设置为true
。这里我更新了package.json
package.json
...
"jest": {
"testEnvironment": "jsdom",
"globals": {
"IS_REACT_ACT_ENVIRONMENT": true
}
}
...
然后,可以更新测试以使用来自act()
的react-dom/test-utils
。
import React from 'react';
import {createRoot} from 'react-dom/client';
import {act} from 'react-dom/test-utils';
import {Appointment} from '../src/Appointment';
describe('Appointment', () => {
it("renders the customer's first name.", () => {
const customer = {firstName: 'Ashley'};
const component = <Appointment customer={customer} />;
const container = document.createElement('div');
document.body.appendChild(container);
const root = createRoot(container);
act(() => root.render(component));
expect(document.body.textContent).toMatch('Ashley');
});
});
资源:
https://reactjs.org/docs/test-utils.html#act
act()
测试实用程序docs: up act()
in React 18:https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#configuring-your-testing-environment发布于 2022-07-17 15:07:41
当您调用ReactDOM.render
时,render 17将立即呈现
const App = () => {
return 'foo';
};
ReactDOM.render(<App />, document.querySelector('.react'));
console.log(document.querySelector('.react').textContent);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div class='react'></div>
相反,Reactive18没有,它只在完成任何其他代码(通常是几毫秒后)之后执行呈现工作:
const App = () => {
return 'foo';
};
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
console.log(document.querySelector('.react').textContent.trim());
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div class='react'></div>
这种差异是看不见的,但是会导致测试失败,因为在expect
运行时呈现还没有发生。
一个选项是use the callback form of the test,这样您就可以在呈现发生后调用expect
。
it("renders the customer's first name.", (done) => {
const component = <Appointment customer={ { firstName: 'Ashley' } } />;
const container = document.body.appendChild(document.createElement('div'));
createRoot(container).render(component);
setTimeout(() => {
expect(document.body.textContent).toMatch('Ashley');
done();
});
});
const App = () => {
return 'foo';
};
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
setTimeout(() => {
console.log(document.querySelector('.react').textContent.trim());
});
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div class='react'></div>
https://stackoverflow.com/questions/73015903
复制相似问题