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:
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:
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With