Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jest test passes but error not caught and logs to console

Tags:

reactjs

jestjs

Is there a way to test custom errors in a component in Jest without there being an Uncaught error thrown in the console?

Here I have a simple button component:

import React from 'react';

export default class Button extends React.Component {
    render() {

        if (!this.props.type) {
            throw new Error('Button requires a type prop');
        }

        return (
            <button className={`btn btn-${this.props.type}`}>Button</button>
        );
    }
}

When the component is used without providing the type property, I want my custom error to be thrown. I also have the following Jest test:

import React from 'react';
import ReactDOM from 'react-dom';
import Button from './button';

it('throws an error if the type prop is not defined', () => {
    const buttonType = undefined;
    const container = document.createElement('div');

    expect(() => {
        ReactDOM.render(<Button type={buttonType} />, container);
    }).toThrow('Button requires a type prop');
});

The unit test passes, however the console produces an error similar to:

console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Uncaught [Error: Button requires a type prop]

The above error occurred in the <Button> component:
in Button (at button.spec.js:20)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/docs/error-boundaries.html to learn more about error boundaries.

Usually in Jasmine, the .toThrow() matcher would automatically catch the error and there would be no logging.

I have read about error boundaries, but these seem to be in the application level, not in the component level.

Am I missing an approach that is better suited to test this?

Edit: Using the following versions:

  • react: 16.2.0
  • react-dom: 16.2.0
  • jest: 22.2.2
like image 387
Dean James Avatar asked Feb 14 '18 11:02

Dean James


1 Answers

The problem is indeed React's error handling prevents the error from bubbling up to the top-level, where Jasmine can detect it. Two possible workarounds:

Monkeypatch console.error to actually throw

console.error = msg => { throw new Error(msg); };

This is obviously hack and would break if React changed to use a different approach for error reporting. stijndepestel's answer is a more robust approach to this.

Create an error boundary just for testing

let errorInfo = null;
class ErrorBoundary extends React.PureComponent {
    state = {hasError: false};

    componentDidCatch(err, info) {
        errorInfo = [err, info]
        this.setState({hasError: true});
    }

    render() {
        if (!this.state.hasError)
            return React.Children.only(this.props.children);
        return 'Errored';
    }
}

You can then wrap your test in a handler like this one and then assert that errorInfo is not null and that it contains an error as you expect

like image 177
Felipe Avatar answered Sep 19 '22 15:09

Felipe