React-testing-library: Clearer documentation regarding cleanUp and having to render the component for each test block

Created on 8 Dec 2018  路  8Comments  路  Source: testing-library/react-testing-library

  • react-testing-library: "^5.2.3",
  • react version: "^16.7.0-alpha.2"
  • node version: 11.3
  • npm (or yarn) version: 1.12.3

Relevant code or config:

config file

// react-testing-library renders your components to document.body,
// this will ensure they're removed after each test.
import 'react-testing-library/cleanup-after-each';
// this adds jest-dom's custom assertions
import 'jest-dom/extend-expect';

test file

const { getByLabelText, getByText } = render(<Login/>);

it('has an input box and hello button', () => {
    getByLabelText(/username/i);
        getByText(/hello/i);
});

it('has an hello button', () => {
    getByText(/hello/i);
});

What you did:

I am trying to test a number of different it blocks in one test file. To begin with I created one

const { getByLabelText, getByText } = render(<Login/>);

However, in the second it block the component is no longer there so the second test fails. the first test passes although the element I am testing in the second block has passed in the first test (just a simple dummy example for this issue).

What happened:

screenshot 2018-12-08 at 20 09 43

Suggested solution:

The issue here was that in my config I had the following.

import 'react-testing-library/cleanup-after-each';

This meant that the component was no longer there in the second test block. This confused me as I assumed it was going to be cleanedUp after all the tests had ran in that test file., not per block

Maybe a note in the documentation here mentioning this. Also perhaps to show that you will need to render the component again in each block.

Most helpful comment

Is there a reason you want to do that in beforeEach instead of render in each block? Do you use the same queries each time?

If you really want you could this I suppose:

let getByText;

beforeEach(() => {
    const queries = render(<Component />);
    getByText = queries.getByText;
});

Another thing is that you can use dom-testing-library queries without binding to a node, using plain imports:

const {getByText} = require('react-testing-library');

let rendered = null;

beforeEach(() => {
    const {container} = render(<Component />);
    rendered = container;
});

test('has some text', () => {
  const myParagraph = getByText(rendered, 'some text')
  expect(myParagraph).not.toBeNull() // some assertions...
})

All 8 comments

I've seen several people try to do this (use several it()/test() blocks for different stages of a flow) - we should add docs about why this isn't a good idea:

  • The unit of isolation in Jest and other test runners (Mocha) is the test block (it or test). afterEach hooks run after each test block, not after every module or describe.
  • A rule of thumb is that each failing test or it should read like a bug report. That makes it easier to debug failed test output - each failure is isolated and describes one flow or unit of functionality. Even if you don't clean up after each test, test blocks that depend on the previous test make it difficult to know if the first part of the chain is problem, or if each piece is failing separately. E.g., if the 5th of 10 blocks fails, it's likely some of the next 5 will also fail for reasons unrelated to the functionality under test. It's ok to have several expect() calls within a flow.

This makes sense to clean everything up after each block. I do agree with that.

But I think it is good to use several it()/test() blocks for different stages of a flow.

You are even kind of saying the same thing here, no?

A rule of thumb is that each failing test or it should read like a bug report. That makes it easier to debug failed test output - each failure is isolated and describes one flow or unit of functionality.

Yes, but at the test level, they need to be independent. If tests depend on each they can't be independent, and can't produce a clear bug report-like statement about why they failed. This because Jest keeps running tests after one fails. A failed expect bails out of the rest of the test, though, so you know where and why the failure happened and don't clutter the report with unrelated failures.

yeah, this makes sense. So to render again from scratch makes sense.

However, this issue with react-testing-library is that you can't use beforeEach due to the way you access getByText etc. As it will be block-scoped it can't be accessed outside. I don't think this was an issue with enzyme (could be wrong here). Maybe there is a way of using beforeEach with react-testing-lib with a different approach I am not aware of. Or maybe none of this is a big issue. Just felt like I was writing the same code over and over.

beforeEach(() => {
    // getByText will not be available outside here 
     const { getByText } = render(<Component />);
});

Is there a reason you want to do that in beforeEach instead of render in each block? Do you use the same queries each time?

If you really want you could this I suppose:

let getByText;

beforeEach(() => {
    const queries = render(<Component />);
    getByText = queries.getByText;
});

Another thing is that you can use dom-testing-library queries without binding to a node, using plain imports:

const {getByText} = require('react-testing-library');

let rendered = null;

beforeEach(() => {
    const {container} = render(<Component />);
    rendered = container;
});

test('has some text', () => {
  const myParagraph = getByText(rendered, 'some text')
  expect(myParagraph).not.toBeNull() // some assertions...
})

I do use the same queries each time. Is this surprising? I would have thought most people would? The solutions above make sense too. I just feel using beforeEach saves me repeating the same code over and over again. Make sense? But if using render in each block is the recommended way to do it, I have no issue with that.

Depends on how similar the tests are. For more integration style tests it's likely you'll need a few different query combinations. If you have highly similar tests, like permutations of props, test.each in Jest can simplify things: https://jestjs.io/docs/en/api#testeachtable-name-fn-timeout

And also if you have things like Portals rendering outside the root node, you're going to need within or an unbound dtl query to rescope the queries to another node. So I'd say the render query binding is more of a convenience thing than the absolute only recommended way of doing things.

Hope that makes sense!

Was this page helpful?
0 / 5 - 0 ratings