getsentry / sentry-javascript

Official Sentry SDKs for JavaScript
https://sentry.io
MIT License
7.97k stars 1.57k forks source link

Testing Sentry.ErrorBoundary results in 3 reports using sentry-testkit #9514

Closed tayfun-corp closed 11 months ago

tayfun-corp commented 11 months ago

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/react

SDK Version

7.77.0

Framework Version

7.77.0

Link to Sentry event

No response

SDK Setup

No response

Steps to Reproduce

I have the following simple jest testcase:

/* eslint-disable no-console */

import { render } from '@testing-library/react';
import { SENTRY_DSN } from 'src/constants';
import * as Sentry from '@sentry/react';
import sentryTestkit from 'sentry-testkit';

const { testkit, sentryTransport } = sentryTestkit();

const ThrowError = () => {
  // eslint-disable-next-line functional/no-throw-statements
  throw Error('test error');
};

describe('Inside ErrorBoundary', () => {
  const consoleError = console.error;
  beforeAll(() => {
    console.error = jest.fn();
    Sentry.init({ dsn: SENTRY_DSN, transport: sentryTransport });
  });
  afterAll(() => {
    console.error = consoleError;
  });

  it('should render Fallback component and captureException when error is thrown', async () => {
    expect(testkit.reports()).toHaveLength(0);
    render(
      <Sentry.ErrorBoundary fallback={<div>some error</div>}>
        <ThrowError />
      </Sentry.ErrorBoundary>
    );
    // While testing this has 3 errors and fails
    expect(testkit.reports()).toHaveLength(1);
  });
});

The test fails because number of reports is 3, not 1. I've read that in dev mode Reacts bubbles up exceptions so maybe I'd see 2, but how come this is 3 during testing? Does dev mode apply to testing too?

Debugger shows these errors:

Screenshot 2023-11-09 at 19 31 28

Expected Result

Exception logged once.

Actual Result

Length of reports is 3.

Lms24 commented 11 months ago

Hi @tayfun-corp thanks for writing in! sentry-testkit is a community-maintained 3rd party library and we don't offer official support for it. Please open an issue in the test-kit repository, thank you!

Closing for the moment. Please let us know if we can help you further, cheers!

tayfun-corp commented 11 months ago

Thanks for the pointers @Lms24 . Would you know about how Sentry's ErrorBoundary works with React? I'm curious as to how there could be multiple messages. The first two exceptions are from my component, the last one is from React's error boundary rethrowing the error I think. Not sure why there are 2 from my custom component though.

Lms24 commented 11 months ago

A wild guess (not the react expert in our team) would be that your ThrowError component is rendered twice for some reason, causing two throws being thrown. Any chance that React Strict mode is active? As to why the error boundary only rethrows one of them, I'm not sure.

cc @AbhiPrasad any ideas why the component throws twice?

You probably know this but just to make sure, you should expect both, the error boundary and the original error in the Sentry payload.

tayfun-corp commented 11 months ago

You probably know this but just to make sure, you should expect both, the error boundary and the original error in the Sentry payload.

@Lms24 I did not know that. So we should expect 2 issues on Sentry, one for the original error in the component and one from the Sentry.ErrorBoundary?

How does that help? My intuition was Sentry.ErrorBoundary would capture one Sentry event with one of the stack frames in there would be the original component?

Lms24 commented 11 months ago

No, sorry, I meant the payload of a Sentry error event. You should expect one issue in the Sentry UI that will contain both errors. The SDK links the original error and the error boundary error together and sends it as one error event to Sentry.

So event.exception.values.length should be 2.

I'm not sure how Sentry Testkit models this within their reports.

AbhiPrasad commented 11 months ago

Yeah there's three total errors here.

  1. The original error that is captured by the error boundary
  2. The original error captured by global event listener. This only occurs in in dev mode, because error boundaries rethrow the errors that they catch and those errors bubble up to the global event listener where it is captured again.
  3. A synthetic react error boundary error that is generated by the sentry react sdk and attached to an error. This synthetic error is not captured as a standalone sentry event, but instead is included as part of the sentry event sent for the original error sent by the error boundary. See https://docs.sentry.io/platforms/javascript/guides/react/features/error-boundary/#linked-errors for more details.

image

2 Sentry events

tayfun-corp commented 11 months ago

That clears it up! Thanks guys, Sentry rocks @Lms24 @AbhiPrasad