44

I would like to have 100% coverage on my project.

img

In order to do so, I need to test my index.js file which is very basic :

index.js

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

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

I can't find how to test this. When creating a function such as :

index.js

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

and then testing it :

index.test.js

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

I get an error when importing index: Invariant Violation: _registerComponent(...): Target container is not a DOM element.

PS:

Note that I already have the following test, working perfectly :

App.test.jsx

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App/>, div);
});
1
  • can you also please clarify in your intent, I am assuming you meant that you do not want to ignore the file just to get 100% coverage. Some of of the answers have suggested suppression, which is not a solution. See my recent answer for React 18 Commented Aug 19, 2022 at 12:41

8 Answers 8

34

If 100% coverage on your project is your goal and the code in your index.js file is trivial, then it might be a good option to exclude the file from the coverage report, as Andreas Köberle points out in his answer.

Create-react-app currently only supports these four keys in the Jest configuration (source):

collectCoverageFrom
coverageReporters
coverageThreshold
snapshotSerializers

This is why

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

won't work.

Add following code to the most outer scope of your package.json file:

"jest": {
  "collectCoverageFrom": [
    "src/**/*.{js,jsx}",
    "!src/index.js"
  ]
}

In the image below you see the output of a test run with this code added to the package.json of the initial app created with create-react-app v1.4.3. Note that the index.js file doesn't show up in the report anymore and also doesn't affect the coverage percentage.

Coverage report

Sign up to request clarification or add additional context in comments.

1 Comment

I am not sure if I agree this is a solution, it sounds like gaming the system to get the results you want, it is a bad practice. Answer by Shiraz should be the accepted answer.
30

This is how I've tested 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);
  });
});

6 Comments

damn -- wish I could get this to work. I keep getting null as the value of div.
@rpivovar, are you saying that document.createElement("div") returns null?
If you're using React strict mode, remember to add wrappers also to the test file: .toHaveBeenCalledWith(<React.StrictMode><App /></React.StrictMode>,div); Otherwise you'll receive <UNDEFINED><App /></UNDEFINED> test failure
This is a great answer it worked, I am going to post a new one that will work for React 18. Thank you!
@0x5929. I have posted the answer for React 18. Please see and at the bottom of the this thread. Thanks!
|
18

The main question is what you want to test there. If you want to test that your code works correct, write a unit test that spies on ReactDOM.render and mocks document.getElementById('root'). Cause this is all your code does, calling ReactDOM.render with our App component and a specific 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(...)
});

If you want test that the app really starts in your page, you should write integration test with Selenium or Nightwatch.js

To just get 100% coverage you can also ignore this file by adding it to the coveragePathIgnorePatterns in your jest settings

6 Comments

What I want to do is either ignore index.js from jest coverage, or actually have a test for my index file in order to have 100% coverage from jest --coverage. adding coveragePathIgnorePatterns": ["src/index.js"] to package.json doesn't work...
Updating my answer, even I would argue that 100% coverage should not be a goal in testing.
react-scripts test --env=jsdom --coverage --collectCoverageFrom=src/**/*.js? --collectCoverageFrom=!src/index.js works, but setting directly collectCoverageFrom in package.json doesn't work. coveragePathIgnorePattern doesn't seem to work at all...
@ThomasSauvajon Including coveragePathIgnorePatterns seems to be working just fine with react-scripts: 1.0.17 (includes react v16)
CreateReactApp application is not supporting coveragePathIgnorePatterns use collectCoverageFrom instead.
|
8

I found an article online that explains this way to write the test...

// index.test.js
import Index from './index.js';

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

Now change the index.js file accordingly:

// 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')
);

Comments

1

Extending on dcastil's answer, here's how to skip these trivial files for a TypeScript project:

  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

By now coverage should be higher.

Comments

1

Here what i did and looks like it works just perfect (100% coverage, app doesn't break):

// 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
);

and then in 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)
  })
})

1 Comment

The second line in your test body looks promising. However, in the first line, you basically do the expected thing, so the second line will be obviously true. You are testing your test. ;) How about removing the first line and letting the implemented code do that call, so your test will actually test, that the implementation and not itself...
1

Added a couple of more test cases. Any feedback would be appreciated...

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();
  });
});

Comments

0

Inspired by the answer above by Shiraz. This is essentially the same solution but for React 17/18. Also it provides some additional test coverage.

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 />,);
  });

});

2 Comments

Getting TypeError : Cannot destructure property 'Consumer' of 'ThemeContext' as it is undefined.
How can I do it with vitest?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.