getsentry / sentry-react-native

Official Sentry SDK for React Native
https://sentry.io
MIT License
1.57k stars 337 forks source link

Testing with Jest: Jest encountered an unexpected token #1261

Closed ghasemikasra39 closed 3 years ago

ghasemikasra39 commented 3 years ago

OS:

Platform:

SDK:

SDK version: 2.0.2

react-native version: 0.63.3

Are you using Expo?

Are you using sentry.io or on-premise?

If you are using sentry.io, please post a link to your issue so we can take a look:

[Link to issue]

Configuration:

(@sentry/react-native)

Sentry.init({
  dsn: env.SENTRY_DSN,
  enableAutoSessionTracking: true,
  debug: isDebugEnable(),
  environment: env.SENTRY_ENV,
  normalizeDepth: 4,
});

I have following issue:

Sentry works perfectly fine in a non-testing environment. However, in a testing environment using Jest and Enzyme, I get this message when running my test:

yarn run v1.22.4
$ jest App.test.tsx
 FAIL  __tests__/App.test.tsx
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /Users/kasra/Documents/youpendo/youpendo-app-bareworkflow/node_modules/@sentry/react-native/dist/js/index.js:1
    export { Severity, Status, } from "@sentry/types";
    ^^^^^^

    SyntaxError: Unexpected token 'export'

    > 1 | import * as Sentry from "@sentry/react-native";
        | ^
      2 | import {env} from '../../config';
      3 |
      4 | Sentry.init({

      at Runtime._execModule (node_modules/jest-runtime/build/index.js:1157:58)
      at Object.<anonymous> (src/services/utility/BugTrackerService.ts:1:1)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        3.944s
Ran all test suites matching /App.test.tsx/i.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

When I add @sentry/react-native to transformIgnorePatterns:

  transformIgnorePatterns: [
    'node_modules/(?!(jest-)?react-native|@sentry/react-native|react-clone-referenced-element|@react-native-community|expo(nent)?|@expo(nent)?/.*|react-navigation|@react-navigation/.*|@unimodules/.*|sentry-expo|native-base|unimodules-permissions-interface|@mollie/.*)',
  ]

I get this message:

yarn run v1.22.4
$ jest App.test.tsx
 FAIL  __tests__/App.test.tsx
  ● Test suite failed to run

    SentryError: Native Client is not available, can't start on native.

      at new SentryError (node_modules/@sentry/utils/src/error.ts:9:5)
      at Object.<anonymous> (node_modules/@sentry/react-native/src/js/wrapper.ts:387:23)
      at Object.<anonymous> (node_modules/@sentry/react-native/src/js/integrations/devicecontext.ts:5:1)

  console.log
    Sentry Logger [Log]: Integration installed: ReactNativeErrorHandlers

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

  console.log
    Sentry Logger [Log]: Integration installed: Release

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

  console.log
    Sentry Logger [Log]: Integration installed: InboundFilters

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

  console.log
    Sentry Logger [Log]: Integration installed: FunctionToString

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

  console.log
    Sentry Logger [Log]: Integration installed: Breadcrumbs

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

  console.log
    Sentry Logger [Log]: Integration installed: LinkedErrors

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

  console.log
    Sentry Logger [Log]: Integration installed: UserAgent

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

  console.log
    Sentry Logger [Log]: Integration installed: DebugSymbolicator

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

  console.log
    Sentry Logger [Log]: Integration installed: RewriteFrames

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

  console.log
    Sentry Logger [Log]: Integration installed: DeviceContext

      at node_modules/@sentry/utils/src/logger.ts:36:22
          at Array.forEach (<anonymous>)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        4.748s
Ran all test suites matching /App.test.tsx/i.

ReferenceError: You are trying to `import` a file after the Jest environment has been torn down.

      at Object.get Alert [as Alert] (node_modules/react-native/index.js:166:12)
      at ReactNativeBackend._showCannotConnectDialog (node_modules/@sentry/react-native/dist/js/backend.js:146:22)
      at ReactNativeBackend._callee$ (node_modules/@sentry/react-native/dist/js/backend.js:132:22)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:45:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:274:22)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

I tried mocking it in __mocks__/react-native-sentry/react-native-sentry.js:

jest.mock('@sentry/react-native', () => ({
  Sentry: {
    setTagsContext: jest.fn(),
    setExtraContext: jest.fn(),
    captureBreadcrumb: jest.fn(),
    captureException: jest.fn(),
    captureMessage: jest.fn()
  }
}));

But I still get the same message as above.

Steps to reproduce:

Commenting out Sentry from the whole app and the error is gone.

environmentt:

   "jest": "^26.4.2",
    "jest-enzyme": "^7.1.2"
    "enzyme": "^3.11.0",
    "enzyme-adapter-react-16": "^1.15.4",

Actual result:

jest returns the error above

Expected result:

The test should not throw any error

jennmueng commented 3 years ago

Have you tried passing enableNative: false to Sentry.init after you get the second error message about not being able to start on native? Also, the first error message about unsupported syntax looks like your Jest isn’t setup to handle ES6 syntax while React Native does.

rineesh-pp commented 3 years ago

I had the similar kind of issue and I resolved that by mocking sentry init function. jest.mock('@sentry/react-native', () => ({ init: () => jest.fn() }));

yungkittty commented 3 years ago

@jennmueng I've tried to use enableNative: false but it doesn't change anything which is weird :

console.log({
  dsn: Config.APP_ENV !== 'dev' ? Config.SENTRY_DSN : undefined,
  debug: Config.APP_ENV !== 'production',
  environment: Config.APP_ENV,
  attachStacktrace: true,
  enableNative: Config.APP_ENV !== 'test',
});

Sentry.init({
  dsn: Config.APP_ENV !== 'dev' ? Config.SENTRY_DSN : undefined,
  debug: Config.APP_ENV !== 'production',
  environment: Config.APP_ENV,
  attachStacktrace: true,
  enableNative: Config.APP_ENV !== 'test',
});

This is the full output :

yarn run v1.22.10
$ jest
 FAIL  __tests__/App-test.tsx
  ● Test suite failed to run

    SentryError: Native Client is not available, can't start on native.

      at new SentryError (node_modules/@sentry/utils/src/error.ts:9:5)
      at Object.<anonymous> (node_modules/@sentry/react-native/src/js/wrapper.ts:387:23)
      at Object.<anonymous> (node_modules/@sentry/react-native/src/js/integrations/devicecontext.ts:5:1)

  console.log
    {
      dsn: undefined,
      debug: true,
      environment: 'test',
      attachStacktrace: true,
      enableNative: false
    }

      at Object.<anonymous> (src/lib/AppTracker.ts:7:9)

  console.warn
    Sentry Logger [Warn]: No DSN provided, backend will not do anything.

      at node_modules/@sentry/utils/src/logger.ts:46:22
      at Object.consoleSandbox (node_modules/@sentry/utils/src/misc.ts:174:18)
      at Logger.warn (node_modules/@sentry/utils/src/logger.ts:45:5)
      at new BaseBackend (node_modules/@sentry/core/src/basebackend.ts:70:14)
      at ReactNativeBackend._createSuperInternal (node_modules/@sentry/react-native/dist/js/backend.js:38:311)
      at new ReactNativeBackend (node_modules/@sentry/react-native/src/js/backend.ts:18:5)
      at new BaseClient (node_modules/@sentry/core/src/baseclient.ts:88:21)

  console.warn
    Sentry Logger [Warn]: No DSN provided, backend will not do anything.

      at node_modules/@sentry/utils/src/logger.ts:46:22
      at Object.consoleSandbox (node_modules/@sentry/utils/src/misc.ts:174:18)
      at Logger.warn (node_modules/@sentry/utils/src/logger.ts:45:5)
      at BrowserBackend.BaseBackend (node_modules/@sentry/core/src/basebackend.ts:70:14)
      at new BrowserBackend (node_modules/@sentry/browser/dist/backend.js:21:38)
      at new ReactNativeBackend (node_modules/@sentry/react-native/src/js/backend.ts:19:28)
      at new BaseClient (node_modules/@sentry/core/src/baseclient.ts:88:21)

  console.warn
    Sentry Logger [Warn]: Warning: No DSN was provided. The Sentry SDK will be disabled. Native SDK will also not be initalized.

      at console.Object.<anonymous>.console.warn (node_modules/react-native/Libraries/LogBox/LogBox.js:36:13)
      at node_modules/@sentry/utils/src/logger.ts:46:22
      at Object.consoleSandbox (node_modules/@sentry/utils/src/misc.ts:174:18)
      at Logger.warn (node_modules/@sentry/utils/src/logger.ts:45:5)
      at Object.call (node_modules/@sentry/react-native/src/js/wrapper.ts:86:14)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:63:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:293:22)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        1.574 s
Ran all test suites.

ReferenceError: You are trying to `import` a file after the Jest environment has been torn down.

      at Object.get Alert [as Alert] (node_modules/react-native/index.js:166:12)
      at ReactNativeBackend._showCannotConnectDialog (node_modules/@sentry/react-native/src/js/backend.ts:118:7)
      at ReactNativeBackend.call (node_modules/@sentry/react-native/src/js/backend.ts:109:12)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:63:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:293:22)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Rafatcb commented 3 years ago

Add the package @sentry/react-native to your package.json in jest.transformIgnorePatterns. In the below example, I'm ignoring react-native and @sentry/react-native:

"transformIgnorePatterns": [
  "node_modules/(?!(react-native|@sentry/react-native)/)"
]
NavinSeth commented 3 years ago

https://github.com/getsentry/sentry-react-native/issues/1261#issuecomment-769345583 - This worked for me.

jennmueng commented 3 years ago

Closing this as mocking Sentry like https://github.com/getsentry/sentry-react-native/issues/1261#issuecomment-769345583 suggested will work fine in Jest testing as you most likely will not need to unit test Sentry. Will add this to the troubleshooting section we're working on for the docs.

pke commented 2 years ago

However this does not work, when tested code actually calls Sentry.captureException. Then the test will fail with Cannot read property 'captureException' of undefined.

Better provide a proper mock?

//__mocks/@sentry/react-native.ts
const Sentry = {
  captureException: jest.fn(),
}

export default Sentry
dmiska25 commented 2 years ago

However this does not work, when tested code actually calls Sentry.captureException. Then the test will fail with Cannot read property 'captureException' of undefined.

Better provide a proper mock?

//__mocks/@sentry/react-native.ts
const Sentry = {
  captureException: jest.fn(),
}

export default Sentry

Also had the issue, just adding the function to the mock worked for me:

jest.mock("sentry-expo", () => ({
  init: () => jest.fn(),
  captureException: () => jest.fn(),
}));
arthwood commented 2 years ago

Just a super minor note on the mocking. It's sufficient to mock the function:

init: jest.fn()

init: () => jest.fn() just overrides init and makes it to return function whereas a real implementation may return something else.

ismoil793 commented 1 year ago

I also had this issue and I had to create a mock file to resolve it

moduleNameMapper: {
  '@sentry/react-native': '<rootDir>/test/__mocks__/sentryMock.js',
},

In sentryMock.js export const captureException = jest.fn();

I left full answer here: https://stackoverflow.com/a/75930289/12924484