Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test React ErrorBoundary

New to React, but not to test applications.

I'd like to make sure every time a component throws a error the ErrorBoundary message is displayed. If you don't know what I mean by ErrorBoundary here is a link.

I'm using Mocha + Chai + Enzyme.

Let's say we need to test React counter example using the following test configuration.

Test Configuration

// DOM
import jsdom from 'jsdom';
const {JSDOM} = jsdom;
const {document} = (new JSDOM('<!doctype html><html><body></body></html>')).window;
global.document = document;
global.window = document.defaultView;
global.navigator = global.window.navigator;

// Enzyme
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });

// Chai
import chai from 'chai';
import chaiEnzyme from 'chai-enzyme';
chai.use(chaiEnzyme());

UPDATE 1 - Some later thoughts

After reading this conversation about the best testing approach for connected components (which touches similar issues) I know I don't have to worry about componentDidCatch catching the error. React is tested enough and that ensures that whenever a error is thrown it will be caught.

Therefore there are only test two tests:

1: Make sure ErrorBoundary displays the message if there's any error

// error_boundary_test.js
import React from 'react';
import { expect } from 'chai';
import { shallow } from 'enzyme';

import ErrorBoundary from './some/path/error_boundary';

describe('Error Boundary', ()=>{
    it('generates a error message when an error is caught', ()=>{
        const component = shallow(<ErrorBoundary />);
        component.setState({
            error: 'error name', 
            errorInfo: 'error info'
        });
        expect(component).to.contain.text('Something went wrong.');
    });
});

2: Make sure component is wrapped inside the ErrorBoundary (in the React counter example is <App />, which is misleading. The idea is to do that on the closest parent component).

Notes: 1) it needs to be done on the parent component, 2) I'm assuming children are simple components, not containers, as it might need more config. Further thoughts: this test could be better written using parent instead of descendents...

// error_boundary_test.js
import React from 'react';
import { expect } from 'chai';
import { shallow } from 'enzyme';

import App from './some/path/app';

describe('App', ()=>{
    it('wraps children in ErrorBoundary', ()=>{
        const component = mount(<App />);
        expect(component).to.have.descendants(ErrorBoundary);
    });
like image 215
a.barbieri Avatar asked Mar 11 '18 13:03

a.barbieri


Video Answer


2 Answers

To test ErrorBoundary component using React Testing Library

const Child = () => {
  throw new Error()
}

describe('Error Boundary', () => {
  it(`should render error boundary component when there is an error`, () => {
    const { getByText } = renderProviders(
      <ErrorBoundary>
        <Child />
      </ErrorBoundary>
    )
    const errorMessage = getByText('something went wrong')
    expect(errorMessage).toBeDefined()
  })
})

renderProviders

import { render } from '@testing-library/react'

const renderProviders = (ui: React.ReactElement) => render(ui, {})
like image 154
Kriti Avatar answered Oct 09 '22 13:10

Kriti


This was my attempt without setting component state:

ErrorBoundary:

import React, { Component } from 'react';
import ErroredContentPresentation from './ErroredContentPresentation';

class ContentPresentationErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

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

  render() {
    return this.state.hasError ? <ErroredContentPresentation /> : this.props.children;
  }
}

export const withErrorBoundary = WrappedComponent =>
  props => <ContentPresentationErrorBoundary>
            <WrappedComponent {...props}/>
          </ContentPresentationErrorBoundary>;

And the test:

it('Renders ErroredContentPresentation Fallback if error ', ()=>{
  const wrappedComponent = props => {
    throw new Error('Errored!');
  };
  const component = withErrorBoundary( wrappedComponent )(props);
  expect(mount(component).html()).toEqual(shallow(<ErroredContentPresentation/>).html());
});
like image 38
wjsc Avatar answered Oct 09 '22 14:10

wjsc