Home > database >  How to test recover from error ErrorBoundry case
How to test recover from error ErrorBoundry case

Time:10-19

Pretty much what the title says.

I have a a test case that looks like this:

  it("recovers from error boundry", async () => {
    const user = userEvent.setup();
    const ThrowError = () => {
      throw new Error("Test");
    };

    render(
      <ErrorBoundary>
        <ThrowError />
      </ErrorBoundary>
    );

    expect(screen.getByTestId("alert")).toBeInTheDocument();

    await user.click(screen.getByRole("button", { name: "Try again?" }));

    expect(screen.queryByRole("alert")).not.toBeInTheDocument();
  });

And an ErrorBoundry which looks like this:

class ErrorBoundary extends Component<ErrorBoundryProps, ErrorBoundryState> {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error: Error, info: ErrorInfo) {
    // IRL would log to an error reporting serive
    //errorService.log({ error, info });
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="app-error-boundry" role="alert">
          <div className="app-error-boundry__content">
            <h1 className="app-error-boundry__title">Something went wrong.</h1>
            <button
              type="button"
              className="app-error-boundry__btn btn"
              onClick={() => this.setState({ hasError: false })}
            >
              Try again?
            </button>
          </div>
        </div>
      );
    }

    return this.props.children;
  }
}

However, in the test the ErrorBoundry is still being found after the button to reset error state is being clicked.

CodePudding user response:

When you reset the error state, the ErrorBoundary component will render this.props.children which is the ThrowError component. The problem is this component will always throw an error when it renders. So the ErrorBoundary will always catch the error, and the error state hasError will always be true. There is no chance of recovering from the error.

You should use some conditions to throw the error from your test component.

E.g.

index.tsx:

import React from "react";

export class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch() { }

  render() {
    if (this.state.hasError) {
      return (
        <div role="alert">
          <div>
            <h1>Something went wrong.</h1>
            <button
              type="button"
              onClick={() => this.setState({ hasError: false })}
            >
              Try again?
            </button>
          </div>
        </div>
      );
    }

    return this.props.children;
  }
}

index.test.tsx:

import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import React, { useState } from 'react';
import { ErrorBoundary } from '.';

describe('74115177', () => {
  test('should pass', () => {
    const ThrowError = () => {
      const [count, setCount] = useState(0);
      if (count > 1) {
        throw new Error("Test");
      }
      return <div>
        <p>count: {count}</p>
        <button onClick={() => setCount(count   1)}>increment</button>
      </div>
    };

    render(
      <ErrorBoundary>
        <ThrowError />
      </ErrorBoundary>
    );

    fireEvent.click(screen.getByRole('button', { name: 'increment' }))
    fireEvent.click(screen.getByRole('button', { name: 'increment' }))

    expect(screen.queryByRole("alert")).toBeInTheDocument();
    fireEvent.click(screen.getByRole("button", { name: "Try again?" }));
    expect(screen.queryByRole("alert")).not.toBeInTheDocument();
  })
})
  • Related