React-testing-library: Tests after a test that throws don't run

Created on 2 Jul 2019  路  6Comments  路  Source: testing-library/react-testing-library

  • react-testing-library 8.0.4:
  • react 16.8.6:
  • node 11.13.0:
  • npm (or yarn) 6.7.0:

Relevant code or config:

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

import "jest-dom/extend-expect";

const ComponentWillThrow = ({ shouldThrow }) => {
    if (shouldThrow) {
        throw new Error("Component did throw after all");
    }
    return <p>I guess no throw this time</p>;
};

afterEach(cleanup);

test("renders without erroring", () => {
    const { getByText } = render(<ComponentWillThrow />);
    expect(getByText("I guess no throw this time")).toBeInTheDocument();
});
test("expect this to throw", () => {
    expect(() => render(<ComponentWillThrow shouldThrow />)).toThrow();
});
test("still render without erroring", () => {
    const { getByText } = render(<ComponentWillThrow />);
    expect(getByText("I guess no throw this time")).toBeInTheDocument();
});

What you did:

I want to assert that a particular React component throws in the render method

What happened:

The test that asserts the component throws passes, but following tests can't render into the document.

Reproduction:

~Couldn't get the example repo to run the tests, tests pass in code sandbox 馃槙 ~
https://github.com/andycarrell/dom-testing-library-throw-reproduction - use yarn not npm 馃槄

help wanted needs investigation

Most helpful comment

I'm a little confused by your reproduction. You should know that when React catches an error in a render method, not only does it rethrow the error, but it also logs to console.error and because you're overriding console.error to throw that seems to be causing errors that prevent the afterEach to run. This appears to be more of a jest bug than anything else (because the afterEach should definitely run).

That said, I think what you probably want to do is to mock out console.error for this test in particular so it doesn't throw because it's actually expected. Here's what I recommend:

// in some setup file:


// Ensure console error fails tests by replacing with a function that throws
const { error: originalError } = console;

beforeAll(() => {
    jest.spyOn(console, 'error').mockImplementation((...args) => {
        originalError(...args)
        const error = util.format.apply(this, rest)
        throw new Error(error)
    })
})

afterAll(() => {
    console.error.mockRestore()
})

afterEach(() => {
    console.error.mockClear()
})

// in your test file:


test('renders without erroring', () => {
    const { getByText } = render(<ComponentWillThrow />);

    expect(getByText('I guess no throw this time')).toBeInTheDocument();
});

test('expect this to throw', () => {
    console.error.mockImplementation(() => {})
    expect(() => render(<ComponentWillThrow shouldThrow />)).toThrow();
    expect(console.error).toHaveBeenCalled()
});

test('still render without erroring', () => {
    const { getByText } = render(<ComponentWillThrow />);

    expect(getByText('I guess no throw this time')).toBeInTheDocument();
});

Regardless of any bugs that may or may not exist, this is the way that I would write this test. Either way I'm pretty confident there's no bug in react-testing-library here, so I'll close this issue. Good luck!

All 6 comments

This is an interesting case. I can't think off the top of my head why it wouldn't work... I'd welcome anyone to investigate further.

As a workaround, you could render your component in a test ErrorBoundary and assert that the ErrorBoundary renders the right stuff based on the error thrown.

A reproduction repo would be nice for debugging this, can you use the template provided in the issue?

Hi @alexkrolick sorry for the delay, I thought here's a reproduction (I couldn't get it working with npm, but yarn is good to go afaik)
https://github.com/andycarrell/dom-testing-library-throw-reproduction

This may be the piece of code which is causing the issue (which replaces console error). I think this is an issue with react testing library, because a simple reproduction without it (using plain JS functions that throw) works as expected

// Ensure console error fails tests by replacing with a function that throws
const { error: originalError } = console;
const throwConsoleErrors = (...rest) => {
    originalError(...rest);
    const error = util.format.apply(this, rest);
    throw new Error(error);
};
console.error = throwConsoleErrors;

I'm a little confused by your reproduction. You should know that when React catches an error in a render method, not only does it rethrow the error, but it also logs to console.error and because you're overriding console.error to throw that seems to be causing errors that prevent the afterEach to run. This appears to be more of a jest bug than anything else (because the afterEach should definitely run).

That said, I think what you probably want to do is to mock out console.error for this test in particular so it doesn't throw because it's actually expected. Here's what I recommend:

// in some setup file:


// Ensure console error fails tests by replacing with a function that throws
const { error: originalError } = console;

beforeAll(() => {
    jest.spyOn(console, 'error').mockImplementation((...args) => {
        originalError(...args)
        const error = util.format.apply(this, rest)
        throw new Error(error)
    })
})

afterAll(() => {
    console.error.mockRestore()
})

afterEach(() => {
    console.error.mockClear()
})

// in your test file:


test('renders without erroring', () => {
    const { getByText } = render(<ComponentWillThrow />);

    expect(getByText('I guess no throw this time')).toBeInTheDocument();
});

test('expect this to throw', () => {
    console.error.mockImplementation(() => {})
    expect(() => render(<ComponentWillThrow shouldThrow />)).toThrow();
    expect(console.error).toHaveBeenCalled()
});

test('still render without erroring', () => {
    const { getByText } = render(<ComponentWillThrow />);

    expect(getByText('I guess no throw this time')).toBeInTheDocument();
});

Regardless of any bugs that may or may not exist, this is the way that I would write this test. Either way I'm pretty confident there's no bug in react-testing-library here, so I'll close this issue. Good luck!

@kentcdodds Fair enough - thanks for the suggestion.

I'll follow up with Jest :)

@kentcdodds I think I have somewhat the same problem here:
https://codesandbox.io/s/bold-tesla-sgeuh

Can you confirm, that this is again, not an issue with react-testing-library itself? Am I doing the testing wrong?

For context: https://spectrum.chat/testing-library/help/testing-if-something-throws-an-error~cf6258fc-3582-402f-b3f7-808d43ff28e2

Was this page helpful?
0 / 5 - 0 ratings