从 ReactDOM.render 切换到 createRoot 使简单的玩笑测试失败

jay*_*ara 5 javascript testing reactjs jestjs react-18

我刚刚开始学习 Daniel Irvine 的《Mastering React Test-Driven Development》,我认为将示例转换为 React 18 应该不会太难。但是我在转换书中的第一个测试时遇到了麻烦使用笑话。

\n

这本书没有使用create-react-app任何东西,而是从头开始构建 React 应用程序,所以我很难找到如何转换代码的相关示例。

\n

当按照书中的 React 17 风格编写时,测试通过了。但如果我替换ReactDOM.render()createRoot(),测试就会失败。

\n

我的应用程序目录如下所示:

\n
\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 package.json\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 package-lock.json\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 Appointment.js\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 test\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 Appointment.test.js\n
Run Code Online (Sandbox Code Playgroud)\n

文件内容是:

\n

package.json

\n
{\n  "name": "appointments",\n  "version": "1.0.0",\n  "description": "Appointments project from Mastering React Test-Driven Development.",\n  "main": "index.js",\n  "scripts": {\n    "test": "jest"\n  },\n  "repository": {\n    "type": "git",\n    "url": "example.com"\n  },\n  "author": "",\n  "license": "ISC",\n  "devDependencies": {\n    "@babel/plugin-transform-runtime": "^7.18.6",\n    "@babel/preset-env": "^7.18.6",\n    "@babel/preset-react": "^7.18.6",\n    "jest": "^28.1.2",\n    "jest-environment-jsdom": "^28.1.3"\n  },\n  "dependencies": {\n    "@babel/runtime": "^7.18.6",\n    "react": "^18.2.0",\n    "react-dom": "^18.2.0"\n  },\n  "jest": {\n    "testEnvironment": "jsdom"\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

src/Appointment.js

\n
import React from \'react\';\n\nexport const Appointment = () => <div>Ashley</div>;\n
Run Code Online (Sandbox Code Playgroud)\n

test/Appointment.test.js

\n
import React from \'react\';\nimport ReactDOM from \'react-dom\';\n// import {createRoot} from \'react-dom/client\';\n\nimport {Appointment} from \'../src/Appointment\';\n\ndescribe(\'Appointment\', () => {\n  it("renders the customer\'s first name.", () => {\n    const customer = {firstName: \'Ashley\'};\n    const component = <Appointment customer={customer} />;\n    const container = document.createElement(\'div\');\n    document.body.appendChild(container);\n\n    ReactDOM.render(component, container);\n\n    // const root = createRoot(container);\n    // root.render(component);\n\n    expect(document.body.textContent).toMatch(\'Ashley\');\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n

使用ReactDOM.render(),测试通过,但出现以下错误:

\n
  console.error\n    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\n\n      11 |     document.body.appendChild(container);\n      12 |\n    > 13 |     ReactDOM.render(component, container);\n         |              ^\n      14 |\n      15 |     expect(document.body.textContent).toMatch(\'Ashley\');\n      16 |   });\n\n      at printWarning (node_modules/react-dom/cjs/react-dom.development.js:86:30)\n      at error (node_modules/react-dom/cjs/react-dom.development.js:60:7)\n      at Object.render (node_modules/react-dom/cjs/react-dom.development.js:29670:5)\n      at Object.render (test/Appointment.test.js:13:14)\n
Run Code Online (Sandbox Code Playgroud)\n

我查找了如何转换ReactDOM.render()createRoot(),并将测试更改为:

\n
import React from \'react\';\n// import ReactDOM from \'react-dom\';\nimport {createRoot} from \'react-dom/client\';\n\nimport {Appointment} from \'../src/Appointment\';\n\ndescribe(\'Appointment\', () => {\n  it("renders the customer\'s first name.", () => {\n    const customer = {firstName: \'Ashley\'};\n    const component = <Appointment customer={customer} />;\n    const container = document.createElement(\'div\');\n    document.body.appendChild(container);\n\n    // ReactDOM.render(component, container);\n\n    const root = createRoot(container);\n    root.render(component);\n\n    expect(document.body.textContent).toMatch(\'Ashley\');\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n

并且测试失败如下:

\n
\n> appointments@1.0.0 test\n> jest\n\n FAIL  test/Appointment.test.js\n  Appointment\n    \xe2\x9c\x95 renders the customer\'s first name. (9 ms)\n\n  \xe2\x97\x8f Appointment \xe2\x80\xba renders the customer\'s first name.\n\n    expect(received).toMatch(expected)\n\n    Expected substring: "Ashley"\n    Received string:    ""\n\n      17 |     root.render(component);\n      18 |\n    > 19 |     expect(document.body.textContent).toMatch(\'Ashley\');\n         |                                       ^\n      20 |   });\n      21 | });\n      22 |\n\n      at Object.toMatch (test/Appointment.test.js:19:39)\n      at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:317:13)\n      at runJest (node_modules/@jest/core/build/runJest.js:407:19)\n      at _run10000 (node_modules/@jest/core/build/cli/index.js:339:7)\n      at runCLI (node_modules/@jest/core/build/cli/index.js:190:3)\n\nTest Suites: 1 failed, 1 total\nTests:       1 failed, 1 total\nSnapshots:   0 total\nTime:        0.979 s, estimated 1 s\nRan all test suites.\n
Run Code Online (Sandbox Code Playgroud)\n

我怎样才能通过使用这个测试createRoot()

\n

jay*_*ara 7

经过更多研究后,我发现act()可以在 React 18 中使用它来强制在检查测试断言之前进行渲染。这允许测试立即运行,而无需在测试失败时等待 Jest 的 did() 超时。

globals.IS_REACT_ACT_ENVIRONMENTtrue必须在 Jest 配置中设置为。我在这里更新了package.json

package.json

...
  "jest": {
    "testEnvironment": "jsdom",
    "globals": {
      "IS_REACT_ACT_ENVIRONMENT": true
    }
  }
...
Run Code Online (Sandbox Code Playgroud)

然后可以更新测试以使用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');
  });
});
Run Code Online (Sandbox Code Playgroud)

资源: