reactjsjestjsreact-hooksreact-testing-libraryreact-error-boundary

How to write a test if React-Error-Boundary recovers from error?


My application fetches from an API, then renders the data. If a non-existing value is inputted, then the error boundary triggers and catches the error.

I'm using ErrorBoundary from the react-error-boundary library and QueryErrorResetBoundary from the react-query library.

With my React-Error-Boundary setup, my application has an error boundary trigger when an error occurs and is able to recover from the error by resetting state. I currently have a passing test for the error boundary triggering when an error occurs. Now I'd like to test if the error boundary can recover from the triggered error boundary and reset state. Please let me know how to go about that with Jest and React Testing Library.

App component

const ErrorFallback = ({ error, resetErrorBoundary }) => {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre style={{ color: "red" }}>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
};

const App = () => {
  const [idQuery, setIdQuery] = useState(0);

  return (
    <div>
      <QueryErrorResetBoundary>
        <ErrorBoundary
          FallbackComponent={ErrorFallback}
          onReset={() => {
            setIdQuery(0);
          }}
          resetKeys={[idQuery]}
        >
          <Home idQuery={idQuery} setIdQuery={setIdQuery} />
        </ErrorBoundary>
      </QueryErrorResetBoundary>
      <ReactQueryDevtools initialIsOpen={true} />
    </div>
  );
};

App.test.js

const App = () => {
    const [state, setState] = useState(0)
    return (
        <QueryErrorResetBoundary>
            <ErrorBoundary
                FallbackComponent={ErrorFallback}
                onReset={() => {
                    setState(0);
                }}
                resetKeys={[state]}
            >
                <Child />
            </ErrorBoundary>
        </QueryErrorResetBoundary>
    )
}

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

describe("Error Boundary", () => {
    beforeEach(() => {
        render(
            <App />
        );
    });

    it("should trigger the Error Boundary when an error occurs", () => {
        const errorMessage = screen.getByTestId("error-boundary-message");
        expect(errorMessage).toBeInTheDocument();
    });

    it("should recover from Error Boundary", () => {
        // ???
    })

});

Solution

  • Without knowing what you're actually trying to do, I'm suspecting something like this will probably help get you started:

    const App = () => {
        const [isRecovered,setIsRecovered] = useState(false)
        return (
            <QueryErrorResetBoundary>
                <ErrorBoundary
                    FallbackComponent={ErrorFallback}
                    onReset={() => {
                        setIsRecovered(true)
                    }}
                    resetKeys={[isRecovered]}
                >
                    <Child isRecovered={isRecovered} />
                </ErrorBoundary>
            </QueryErrorResetBoundary>
        )
    }
    
    const Child = ({isRecovered}) => {
        if(!isRecovered) throw new Error();
        return 'All Good';
    }
    

    At the very least you could add the code for ErrorBoundary to your question.