How to write test case for ErrorBoundary in React using Jest / Enzyme

Sylwek picture Sylwek · Apr 7, 2018 · Viewed 13k times · Source

I have been trying (without success) to write a test case for ErrorBoundary component that is handling errors via componentDidCatch lifecycle method. Despite the error produced by child component inside the <ErrorBoundry> component, <ErrorBoundry> does not render info about error in code but the content of faulty component if it would work correct. Component works as expected in production/development but not when it is executed by Jest / Enzyme for testing.

Error from testing:

 PASS  src/ErrorBoundary.test.js
  ● Console

    console.error node_modules/fbjs/lib/warning.js:33
      Warning: `value` prop on `input` should not be null. Consider using an empty string to clear the component or `undefined` for uncontrolled components.
          in input (at ErrorBoundary.test.js:11)
          in div (at ErrorBoundary.test.js:10)
          in ComponentWithError (at ErrorBoundary.test.js:26)
          in ErrorBoundry (created by WrapperComponent)
          in WrapperComponent
    console.log src/ErrorBoundary.test.js:29
      <ErrorBoundry>
        <ComponentWithError>
          <div>
            <input type="text" value={{...}} />
          </div>
        </ComponentWithError>
      </ErrorBoundry>

ErrorBoundry.js:

import React, { Component } from 'react'
import Raven from 'raven-js'
import { Segment, Button } from 'semantic-ui-react'

export default class ErrorBoundry extends Component {
    state = {
        hasError: false
    }

    componentDidCatch(error, info) {
        this.setState({ hasError: true })
        Raven.captureException(error, { extra: info });
    }

    render() {
        if(this.state.hasError) {
            return (
                <div className='error-boundry'>
                    <Segment>
                        <h2> Oh no! Somethin went wrong </h2>
                        <p>Our team has been notified, but click  
                            <Button  onClick={() => Raven.lastEventId() && Raven.showReportDialog()}> 
                            here </Button> to fill out a report.
                        </p>
                    </Segment>
                </div>
            );
        } else {
            return this.props.children;
        }
    }
}

ErrorBoundry.test.js:

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import renderer from 'react-test-renderer'
import { shallow, mount } from 'enzyme'
import ErrorBoundary from './ErrorBoundary'

class ComponentWithError extends Component {
  render() {
    return (
      <div>
        <input type = "text" value = {null}/>  
      </div>
    );
  }
}

describe('<ErrorBoundary> window',()=> {
  it('should match the snapshot', () => {
    const tree = renderer.create(<ErrorBoundary>Test</ErrorBoundary> ).toJSON()
    expect(tree).toMatchSnapshot()
  })

  it('displays error message on error generated by child', () => {
    const wrapper = mount(
      <ErrorBoundary > 
        <ComponentWithError />
      </ErrorBoundary> 
      )
    console.log(wrapper.debug() )
  })
})

Answer

Daniil Ivanov picture Daniil Ivanov · Feb 28, 2019

Enzyme has simulateError helper now.

So this works very well for me:

const Something = () => null;

describe('ErrorBoundary', () => {
  it('should display an ErrorMessage if wrapped component throws', () => {
    const wrapper = mount(
      <ErrorBoundary>
        <Something />
      </ErrorBoundary>
    );

    const error = new Error('test');

    wrapper.find(Something).simulateError(error);

    /* The rest fo your test */
  }
}