测试一个 create-react-app 的 index.js 文件。

43

我希望我的项目能够实现100%的覆盖率。

img

为了达到这个目标,我需要测试我的index.js文件,它非常基础:

index.js

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(<App/>, document.getElementById('root'));

我不知道如何测试这个功能。 创建一个函数,例如:

index.js

const index = (div) => {
  ReactDOM.render(<App />, div || document.getElementById('root'));
};

然后进行测试:

index.test.js

it('renders without crashing', () => {
  const div = document.createElement('div');
  index(div);
});

当我导入index时,出现错误: Invariant Violation: _registerComponent(...): Target container is not a DOM element.

PS:

请注意,我已经拥有以下测试,并且完美运行:

App.test.jsx

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App/>, div);
});

请问您能否澄清一下您的意图,我假设您的意思是您不想忽略文件但仍要达到100%的覆盖率。有些回答建议使用抑制,但这并不是一个解决方案。请参见我最近关于React 18的回答。 - MG Developer
8个回答

34
如果你的项目目标是100%覆盖率,而且你的index.js文件中的代码很简单,那么像Andreas Köberle在他的回答中指出的那样,将该文件从覆盖率报告中排除可能是一个好选择。
Create-react-app目前仅支持Jest配置中的这四个键(source): collectCoverageFrom coverageReporters coverageThreshold snapshotSerializers 这就是为什么

coveragePathIgnorePatterns": ["src/index.js"]

不起作用的原因。
将以下代码添加到您的package.json文件的最外层范围:
"jest": {
  "collectCoverageFrom": [
    "src/**/*.{js,jsx}",
    "!src/index.js"
  ]
}

在下面的图像中,您可以看到使用此代码添加到使用create-react-app v1.4.3创建的初始应用程序的package.json的测试运行的输出。请注意,index.js文件不再出现在报告中,并且也不会影响覆盖率百分比。

Coverage report


我不确定我是否同意这是一个解决方案,它听起来像是操纵系统以获得你想要的结果,这是一种不好的做法。Shiraz的回答应该被接受为正确答案。 - MG Developer

30

这是我测试 index.js 的方法。

index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(<App />, document.getElementById("root"));

index.test.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

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

describe("Application root", () => {
  it("should render without crashing", () => {
    const div = document.createElement("div");
    div.id = "root";
    document.body.appendChild(div);
    require("./index.js");
    expect(ReactDOM.render).toHaveBeenCalledWith(<App />, div);
  });
});

该死 -- 希望我能让它工作。我一直得到 null 作为 div 的值。 - rpivovar
@rpivovar,你是在说document.createElement("div")返回null吗? - Shiraz
8
如果您正在使用React严格模式,请记得在测试文件中添加包装器:.toHaveBeenCalledWith(<React.StrictMode><App /></React.StrictMode>,div);否则,您将收到<UNDEFINED><App /></UNDEFINED>测试失败信息。 - Michal Svorc
1
这是一个很棒的答案,它起作用了。我将发布一个新的答案,它将适用于React 18。谢谢! - MG Developer
@MGDeveloper 我一定会期待这个。我目前正在尝试测试我的 src/index.js 文件,我在这里做了大部分的路由,你有什么想法吗?类似于这样:https://codesandbox.io/s/headless-tree-5jev9n?file=/src/App.js - 0x5929
1
@0x5929。我已经发布了React 18的答案。请查看本帖底部。谢谢! - MG Developer

18
主要问题是你想在那里进行测试。如果你想测试你的代码是否正确运行,请编写一个单元测试,对ReactDOM.render进行监视并模拟document.getElementById('root')。因为这是你的代码所做的一切,调用ReactDOM.render与我们的应用程序组件和特定的div
import ReactDOM from 'react-dom';
...
jest.mock('react-dom', ()=> ({render: jest.fn()}))


it('renders without crashing', () => {

  const div = document.createElement('div');
  ReactDOM.render(<App/>, div);
  global.document.getElementById = (id) => id ==='root' && div
  expect(ReactDOM.render).toHaveBeenCalledWith(...)
});

如果您想测试应用程序是否真的在您的页面中启动,您应该使用Selenium或Nightwatch.js编写集成测试。
为了获得100%的覆盖率,您也可以将此文件添加到Jest设置中的coveragePathIgnorePatterns以忽略它。

我想要做的是要么忽略jest覆盖范围中的index.js,要么实际上为我的index文件编写一个测试,以便从jest --coverage获得100%的覆盖率。将coveragePathIgnorePatterns": ["src/index.js"]添加到package.json中并不起作用... - Thomas Sauvajon
3
更新我的答案,我甚至会认为在测试中100%的覆盖率不应该是一个目标。 - Andreas Köberle
react-scripts test --env=jsdom --coverage --collectCoverageFrom=src/**/*.js? --collectCoverageFrom=!src/index.js 可以正常工作,但是在 package.json 中直接设置 collectCoverageFrom 却无法生效。coveragePathIgnorePattern 似乎根本不起作用... - Thomas Sauvajon
1
@ThomasSauvajon 包括 coveragePathIgnorePatterns 在内,与 react-scripts: 1.0.17(包括 react v16)一起使用似乎完全正常。 - Prancer
1
CreateReactApp 应用程序不支持 coveragePathIgnorePatterns 用法,请使用 collectCoverageFrom 替代。 - RRR

8
我在网上找到一篇文章,讲解了这种编写测试的方法... 文章链接
// index.test.js
import Index from './index.js';

it('renders without crashing', () => {
  expect(JSON.stringify(
    Object.assign({}, Index, { _reactInternalInstance: 'censored' })
  )).toMatchSnapshot();
});

现在请相应地更改index.js文件:
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

export default ReactDOM.render(
  <App />,
  document.getElementById('root') || document.createElement('div')
);

1

dcastil的回答的基础上,以下是如何跳过TypeScript项目中这些琐碎文件的方法:

  1. Edit package.json
  2. At the root level add the following snippet

    {
      ...rest of existing props,
      "jest": {
        "collectCoverageFrom": [
          "src/**/*.{ts,tsx}",
          "!src/serviceWorker.ts",
          "!src/index.tsx"
        ]
      }
    }
    
  3. Save and re-run coverage

到现在为止,覆盖率应该更高。

0

受Shiraz上面的答案启发,这本质上是相同的解决方案,但适用于React 17/18。此外,它提供了一些额外的测试覆盖。

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode />,
);

import React from 'react';
import ReactDOM from 'react-dom';

const render= jest.fn().mockName('render');

jest.mock('react');
jest.mock('react-dom', () => ({
  createRoot: jest.fn().mockName('createRoot')
}));

let documentSpy=jest.spyOn(document, 'getElementById')


describe('Entry point index test', () => {

  const doc =document.createElement('div');
  doc.setAttribute('id', 'root');

  beforeEach(() => {
    ReactDOM.createRoot.mockReturnValue({render});
    require("../index.js");
   });

  it('should call ReactDOM.createRoot once', () => {
    expect(ReactDOM.createRoot).toHaveBeenCalledTimes(1);
  });

  it('should call document.getElementById with root once', () => {
    expect(documentSpy).toHaveBeenCalledTimes(1);
    expect(documentSpy).toHaveBeenCalledWith('root');
  });

  it('should call render with React.StrictMode', () => {
    expect(render).toHaveBeenCalledTimes(1);
    expect(render).toHaveBeenCalledWith( <React.StrictMode />,);
  });

});


获取 TypeError: Cannot destructure property 'Consumer' of 'ThemeContext' as it is undefined. - ClassHacker

0
添加了几个测试用例。欢迎任何反馈...

import React from "react";
import { render, cleanup } from "@testing-library/react";
import ReactDOM from "react-dom";
import App from "./App";

afterEach(cleanup);

// jest.mock will mock all the function using jest.fn() that is present inside the react-dom library
jest.mock("react-dom");

describe("Testing Application Root", () => {
  it("should render without crashing", () => {
      const div = document.createElement("div");
      div.id = "root";
      document.body.appendChild(div);
      require("./index");
      expect(ReactDOM.render).toHaveBeenCalledWith(<App />, div);
  });
    
  it("should render the app inside div which has root id", () => {
    expect(global.document.getElementById("root")).toBeDefined();
  });
  
  it("should render App component", () => {
    expect(App).toBeDefined();
  });
});


0

这是我所做的,看起来完美无缺(100% 覆盖率,应用程序不会崩溃):

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import './index.css';

export const ReactStrictMode = <React.StrictMode>
  <App />
</React.StrictMode>

export const rootElement = document.getElementById('root')

ReactDOM.render(
  ReactStrictMode,
  rootElement
);

然后在index.spec.js中:

// src/index.spec.js
/* eslint-env jest */
import React from 'react'
import ReactDOM from 'react-dom'
import { ReactStrictMode, rootElement } from './index'

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

describe('index.js', () => {
  it('renders without crashing', () => {
    ReactDOM.render(ReactStrictMode, rootElement)
    expect(ReactDOM.render).toHaveBeenCalledWith(ReactStrictMode, rootElement)
  })
})


你测试体中的第二行看起来很有前途。然而,在第一行中,你基本上做了预期的事情,因此第二行显然是正确的。你正在测试你的测试。 ;) 如果删除第一行并让实现的代码进行调用,那么你的测试将实际测试实现而不是自身... - Zoltan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接