javascriptreactjsreact-nativeunit-testingreact-native-testing-library

React Native Unit Testing


I have a few questions about react-native-testing-library.

I want to implement unit tests on my app by using react-native-testing-library.

I run my LoginScreen component using the render() method and check the rendering status of any component from it.

Although it is not in my test class, it is stated that libraries such as Redux, AsyncStorage, Messaging etc. That I have used in LoginScreen should be mocked. An example test class;

describe('LoginScreen', () => {
  it('renders login form correctly', () => {
    const { getByTestId } = render(<LoginScreen />);

    // Check if necessary elements are rendered
    expect(getByTestId('loginButton')).toBeTruthy();
  });
});

My Mocks is;

/* eslint-disable no-undef */
import '@testing-library/jest-native/extend-expect';

jest.mock('@react-native-async-storage/async-storage', () =>
  require('@react-native-async-storage/async-storage/jest/async-storage-mock'),
);

jest.mock('@react-navigation/native', () => ({
  useNavigation: jest.fn(),
  useRoute: jest.fn(),
  useIsFocused: jest.fn(),
  useFocusEffect: jest.fn().mockImplementation(callback => callback()),
}));

jest.mock('@react-native-firebase/messaging', () => ({
  default: jest.fn(() => ({
    getToken: jest.fn(() => Promise.resolve('mocked-token')),
    onMessage: jest.fn(),
    onNotificationOpenedApp: jest.fn(),
    onNotification: jest.fn(),
  })),
}));

jest.mock('react-native-device-info', () => ({
  getDeviceName: jest.fn(() => Promise.resolve('mocked-device-name')),
  getDeviceId: jest.fn(() => Promise.resolve('mocked-device-id')),
}));

I do not understand this. Why do I have to mock services like Redux, AsyncStorage, and Messaging even in any simple test? And after all these mockings, I get an error like this. I have no idea how to solve this;

  ● LoginScreen › renders login form correctly

    TypeError: (0 , _messaging.default) is not a function

       99 |
      100 |   const checkIfAppOpenedByNotification = async () => {
    > 101 |     await messaging()
          |                    ^
      102 |       .getInitialNotification()
      103 |       .then(async (remoteMessage: any) => {
      104 |         if (remoteMessage) {

      at src/screens/LoginScreen.tsx:101:20
      at asyncGeneratorStep (node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
      at _next (node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:9)
      at node_modules/@babel/runtime/helpers/asyncToGenerator.js:27:7
      at node_modules/@babel/runtime/helpers/asyncToGenerator.js:19:12
      at apply (src/screens/LoginScreen.tsx:100:39)
      at checkIfAppOpenedByNotification (src/screens/LoginScreen.tsx:78:15)
      at asyncGeneratorStep (node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
      at _next (node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:9)
      at node_modules/@babel/runtime/helpers/asyncToGenerator.js:27:7
      at node_modules/@babel/runtime/helpers/asyncToGenerator.js:19:12
      at apply (src/screens/LoginScreen.tsx:79:8)

Solution

  • As I know, you are using RNFirebase messaging. For sure the post needs more details about installing @react-native-firebase/messaging and how you import and use the messaging function.

    If your codes are based on docs and still the app is getting errors try to re-mock the messaging function by the following:

    jest.mock('../node_modules/@react-native-firebase/messaging', () => ({
      __esModule: true,
      default: () => Promise.resolve({}),
    });