Jest 测试使用新版本 18 创建 React APP index.js

Jest test Create React APP index.js with new version 18

我有这个 index.js 用于 React 版本 <= 17。

import React from 'react';
import ReactDOM from 'react-dom';
import App from './views/App';
import reportWebVitals from './reportWebVitals';
import './assets/scss/app.scss';
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux';
import store from "./store/index";

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider store={store}>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

这是测试:

import React from "react";
import ReactDOM from "react-dom";
import App from "../views/App";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import store from "../store/index";

jest.mock("react-dom", () => ({ render: jest.fn() }));

describe('Test index.js', () => {
  it("Should render app without crashing", () => {
    const div = document.createElement("div");
    div.id = "root";
    document.body.appendChild(div);
    require('../index.js');
    expect(ReactDOM.render).toHaveBeenCalledWith(
      <React.StrictMode>
        <BrowserRouter>
          <Provider store={store}>
            <App />
          </Provider>
        </BrowserRouter>
      </React.StrictMode>,
      div
    );
  });
});

现在,对于版本 18,我需要将 ReactDOM.render 迁移到 createRoot:

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './views/App';
import reportWebVitals from './reportWebVitals';
import './assets/scss/app.scss';
import { BrowserRouter } from "react-router-dom";
import { Provider } from 'react-redux';
import store from "./store/index";

const container = document.getElementById('root');
const root = createRoot(container);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider store={store}>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>
);

但难的是考试

我试过

import React from "react";
import { createRoot } from 'react-dom/client';
import App from "../views/App";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import store from "../store/index";

describe('Test index.js', () => {

  it("Should render app without crashing", () => {
    const div = document.createElement("div");
    div.id = "root";
    document.body.appendChild(div);
    require('../index.js');
    expect(root.render).toHaveBeenCalledWith(
      <React.StrictMode>
        <BrowserRouter>
          <Provider store={store}>
            <App />
          </Provider>
        </BrowserRouter>
      </React.StrictMode>
    );
  });
});

但是我进了shell

Test index.js
    ✕ Should render app without crashing (10 ms)

  ● Test index.js › Should render app without crashing

    expect(received).toHaveBeenCalledWith(...expected)

    Matcher error: received value must be a mock or spy function

    Received has value: undefined

      13 |     document.body.appendChild(div);
      14 |     require('../index.js');
    > 15 |     expect(root.render).toHaveBeenCalledWith(
         |                         ^
      16 |       <React.StrictMode>
      17 |         <BrowserRouter>
      18 |           <Provider store={store}>

      at Object.<anonymous> (src/__tests__/index.test.js:15:25)

我确实也尝试在 describe

之前添加 jest.mock("react-dom/client");
[...]
jest.mock("react-dom/client");

describe('Test index.js', () => {
[...]

但是我明白了

Test index.js
    ✕ Should render app without crashing (6 ms)

  ● Test index.js › Should render app without crashing

    TypeError: Cannot read properties of undefined (reading 'render')

      10 | const container = document.getElementById('root');
      11 | const root = createRoot(container);
    > 12 | root.render(
         |      ^
      13 |   <React.StrictMode>
      14 |     <BrowserRouter>
      15 |       <Provider store={store}>

      at Object.<anonymous> (src/index.js:12:6)
      at Object.<anonymous> (src/__tests__/index.test.js:17:5)

如何为新 index.js 编写测试?

最后我解决了:

import index from "../index";

describe('Test index.js', () => {

    it("Should render app without crashing", () => {
        expect(
            JSON.stringify(
              Object.assign({}, index, { _reactInternalInstance: 'censored' }),
            ),
          ).toMatchSnapshot();
    });
});

这是修改后的 index.js 文件:

const container = document.getElementById('root') || document.createElement('div');
const root = createRoot(container);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider store={store}>
        <App />
      </Provider>
    </BrowserRouter>
  </React.StrictMode>
);