3

I'm using React Testing Library with Jest to test my React/Redux App.

I want to test the same component in multiple tests without the component's state being shared across each instance.

Something like this...

import React from "react";
import {renderWithRedux} from '../testUtils';
import App from "../components/App";

describe("App", () => {

    test("does something as expected", async () => {
        const {container} = renderWithRedux(<App />);
        // interact with App
    });

    test("does something ELSE as expected", async () => {
        const {container} = renderWithRedux(<App />); //I DONT WANT THE STATE FROM PREVIOUS TEST
    });

});

The problem I'm running into is that the state of the first <App /> is "leaking" into the next test and I want each of my tests to be independent. What is the right way to accomplish this?

Here is the definition of renderWithRedux:

import React from "react";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from 'redux';
import {reducer, initialState} from "./store";
import thunkMiddleware from 'redux-thunk';
import { render } from '@testing-library/react';
import { Router } from 'react-router-dom'
import { createMemoryHistory } from 'history'

function renderWithRedux(
  ui,
  { initialState, store = createStore(reducer, initialState, applyMiddleware(thunkMiddleware)) } = {}
) {
  return {
    ...render(<Provider store={store}>{ui}</Provider>),
    store
  }
}

package.json:

{
  "dependencies": {
    "react": "^16.11.0",
    "react-dom": "^16.11.0",
    "react-redux": "^7.1.3",
    "react-router-dom": "^5.1.2",
    "react-scripts": "2.1.5",
    "redux": "^4.0.4",
    "redux-thunk": "^2.3.0"
  },
  "devDependencies": {
    "@testing-library/jest-dom": "^4.2.3",
    "@testing-library/react": "^9.3.2",
    "redux-mock-store": "^1.5.3",
    "typescript": "^3.7.2"
  }
}
emersonthis
  • 32,822
  • 59
  • 210
  • 375

3 Answers3

3

This can be accomplished by passing state via the initialState argument that your renderWithRedux function is already accepting. For more info on using Redux with React Testing Library, the Redux docs have a great overview - check it out!

Also, we can remove the asyncs here until we have something we're awaiting and we can probably remove the describe function as well. As KCD says, "avoid nesting when you're testing".

import React from 'react';
import { renderWithRedux } from '../testUtils';
import App from '../components/App';

test('does something as expected', () => {
    const { container } = renderWithRedux(<App />, {
        initialState: {
            superhero: 'Spiderman',
        },
    });
});

test('does something ELSE as expected', () => {
    const { container } = renderWithRedux(<App />, {
        initialState: {
            superhero: 'Wonder Woman',
        },
    });
});

As for using cleanup, React Scripts uses Jest as its test runner. Per the React Testing Library docs, Jest uses React Testing Library's cleanup automatically, so there's no need to add that step in this case!

josephemswiler
  • 401
  • 4
  • 5
2

This issue is fixed in v9.0.0. From the docs:

Please note that this is done automatically if the testing framework you're using supports the afterEach global (like mocha, Jest, and Jasmine). If not, you will need to do manual cleanups after each test.

I ran into the same problem with an earlier version of @testing-library/react, and the solution was to use cleanup:

import { render, cleanup } from '@testing-library/react'

describe('...', () => {
  afterEach(cleanup)

  it('...', () => {
    // ...
  })
})

From the docs:

Failing to call cleanup when you've called render could result in a memory leak and tests which are not "idempotent" (which can lead to difficult to debug errors in your tests).

Again, though, in v9+, this is done automatically.

jessepinho
  • 5,580
  • 1
  • 19
  • 19
-1

You can use the beforeEach hook of jest to reinstantiate the component every-time.

https://jestjs.io/docs/en/setup-teardown#repeating-setup-for-many-tests

Your test block would look like as follows.

import React from "react";
import { renderWithRedux } from "../testUtils";
import App from "../components/App";

describe("App", () => {
  let component;

  beforeEach(() => {
    component = <App />;
  });
  test("does something as expected", async () => {
    const { container } = renderWithRedux(component);
    // interact with App
  });

  test("does something ELSE as expected", async () => {
    const { container } = renderWithRedux(component);
  });
});

johnny peter
  • 4,634
  • 1
  • 26
  • 38