Usage with <Sentry.ErrorBoundary>
The @sentry/react
library exposes a <Sentry.ErrorBoundary>
React component that can be used to catch errors in React components, similar to the componentDidCatch
lifecycle method.
However, the <Sentry.ErrorBoundary>
component causes Sentry to send multiple events due to the way it works under the hood. This is because it catches errors in the componentDidCatch
lifecycle method, and then re-throws them, which causes Sentry to send another event. This is not a problem in production, but it can be a problem when testing.
3 Errors are sent to Sentry
When using sentry-testkit
to test Sentry reports, the testkit.reports()
call will return the following 3 total errors, as explained in Sentry's issue #9514:
- The original error that is captured by the error boundary
- 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.
- 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 Linked Errors for more details.
Internally, sentry-testkit
uses the testkit.reports()
call to find the report for the original error, so it will return the first error in the list above. This is the error that you should use to test your application.
Event A -> original error (1) + synthetic error boundary error (3)
Event B -> original error from global handlers (2)
Test Code Example
import React from 'react'
import '@testing-library/jest-dom'
import { render } from '@testing-library/react'
import * as Sentry from '@sentry/react'
import sentryTestkit from 'sentry-testkit'
const { testkit, sentryTransport } = sentryTestkit()
const ThrowError = () => {
// eslint-disable-next-line
throw Error('test error')
}
describe('Inside ErrorBoundary', () => {
const consoleError = console.error
beforeAll(() => {
console.error = jest.fn()
Sentry.init({
dsn: 'https://acacaeaccacacacabcaacdacdacadaca@sentry.io/000001',
transport: sentryTransport,
})
})
afterAll(() => {
console.error = consoleError
})
test('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>
)
expect(testkit.reports()).toHaveLength(3)
})
})
What Else Can Be Done About It?
You can alway customize it through Sentry's Integrations mechanism.
For example, you can disable the GlobalHandler
Integration by doing the following:
import * as Sentry from '@sentry/react'
import { Integrations } from '@sentry/tracing'
Sentry.init({
// ...
integrations: function (integrations) {
return integrations.filter(function (integration) {
return integration.name !== "GlobalHandlers";
});
},
});
You can also take a look on the CaptureConsole
Integration, which is responsible for capturing console errors. You can disable it by doing the following:
import * as Sentry from '@sentry/react'
import { Integrations } from '@sentry/tracing'
Sentry.init({
// ...
integrations: function (integrations) {
return integrations.filter(function (integration) {
return integration.name !== "CaptureConsole";
});
},
});
Find more information about Sentry's Integrations here.