1

I have the following component which displays a text on the app if the app is offline.

import React from 'react';

import { useNetInfo } from '@react-native-community/netinfo';

import { Label } from 'components/ui';

const OfflineNotice = () => {
  const netInfo = useNetInfo();

  if (netInfo.type !== 'unknown' && netInfo.isInternetReachable === false) {
    return <Label size={18} text='No Internet Connection' />;
  }

  return null;
};

export default OfflineNotice;

I want to write a unit test to this to check if this works properly. How can I do this? Im new to unit tests. I don't understand how to mock this.

I use typescript and testing-library/react-native.

UPATED: Why does this first test fail? It should NOT TO BE NULL. But it fails. The error is,

OfflineNotice component › test

expect(received).not.toBeNull()

Received: null

  15 | 
  16 |     const { queryByText } = render(<OfflineNotice />);
> 17 |     expect(queryByText(/no internet connection/i)).not.toBeNull();
     |                                                        ^
  18 |   });
  19 | 

  at Object.<anonymous> (src/components/offline-notice/offline-notice.test.tsx:17:56)
Shashika Virajh
  • 8,497
  • 17
  • 59
  • 103

3 Answers3

4

Cruising the react-native-netinfo github repo, troubleshooting section

You should then add the following to your Jest setup file to mock the NetInfo Native Module:

import mockRNCNetInfo from '@react-native-community/netinfo/jest/netinfo-mock.js';

jest.mock('@react-native-community/netinfo', () => mockRNCNetInfo);

Their mock for testing is:

const defaultState = {
  type: 'cellular',
  isConnected: true,
  isInternetReachable: true,
  details: {
    isConnectionExpensive: true,
    cellularGeneration: '3g',
  },
};

const RNCNetInfoMock = {
  configure: jest.fn(),
  fetch: jest.fn(),
  addEventListener: jest.fn(),
  useNetInfo: jest.fn(),
};

RNCNetInfoMock.useNetInfo.mockResolvedValue(defaultState);

Given this I think you could craft your own mock resolved values in each unit test case:

import { useNetInfo } from '@react-native-community/netinfo';

jest.mock('@react-native-community/netinfo', () => {
  useNetInfo: jest.fn(),
});

...

// Happy path test, known type and internet unreachable
useNetInfo.mockResolvedValueOnce({
  type: 'test', // not 'unknown'
  isInternetReachable: false,
});
// assert render non-null
const { queryByText } = render(<OfflineNotice />);
expect(queryByText(/no internet connection/i)).not.toBeNull();

...

// Sad path test, known type and internet reachable
useNetInfo.mockResolvedValueOnce({
  type: 'test', // not 'unknown'
  isInternetReachable: true,
});
// assert render null
const { queryByText } = render(<OfflineNotice />);
expect(queryByText(/no internet connection/i)).toBeNull();

...

// Sad path test, unknown type and internet unreachable
useNetInfo.mockResolvedValueOnce({
  type: 'unknown',
  isInternetReachable: false,
});
// assert render null
const { queryByText } = render(<OfflineNotice />);
expect(queryByText(/no internet connection/i)).toBeNull();

...

// Sad path test, unknown type and internet reachable
useNetInfo.mockResolvedValueOnce({
  type: 'test', // not 'unknown'
  isInternetReachable: true,
});
// assert render null
const { queryByText } = render(<OfflineNotice />);
expect(queryByText(/no internet connection/i)).toBeNull();

React-Native-Testing-Library

React-Testing-Library

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Thanks @Drew for the detailed answer. One last question. I have updated the question with the error I get. – Shashika Virajh Nov 16 '20 at 09:58
  • @Shashika Could you include your tests with your update so we can see how you've instantiated the mock and each test case? – Drew Reese Nov 16 '20 at 16:38
  • Drew, I added everything as an answer to this post. Could you please tell me what am I doing wrong here? – Shashika Virajh Nov 16 '20 at 17:08
  • @Shashika If you pass a `text` prop and hardcode a regex for it in the test does it work? Also, the `getByX` queries will throw an error if there is no match, whereas the `queryByX` will return null. – Drew Reese Nov 16 '20 at 18:12
  • Thanks Drew. I tried with a hardcoded text. still the same issue. No instances found with text – Shashika Virajh Nov 16 '20 at 18:27
  • @Shashika I've found that "text" can be a little finicky, it is why I use case-insensitive regex if i do. Could you try adding a test id (`data-testid="..."`) to the `Label` and target that with `queryByTestId`? Outside of this I'm at about the limit of my React-Native knowledge and ability to remote debug (in other words I would need a live, working example, to continue debugging in). – Drew Reese Nov 16 '20 at 18:34
  • I tried that as well. Didnt work. Shall I share my repository with you? If you are able to check in case if you have time. – Shashika Virajh Nov 16 '20 at 18:45
0

Now my tests are working. But it gives the unexpected outputs.

COMPONENT:

import React from 'react';

import { useNetInfo } from '@react-native-community/netinfo';

import { Label } from 'components/ui';
import strings from './offline-notice.strings';
import styles from './offline-notice.styles';

const OfflineNotice = ({ style, text }: IProps) => {
  const netInfo = useNetInfo();

  if (netInfo.type !== 'unknown' && netInfo.isInternetReachable === false) {
    return <Label size={18} style={[styles.label, style]} text={text} />;
  }

  return null;
};

OfflineNotice.defaultProps = {
  text: strings.NO_INTERNET_CONNECTION,
};

interface IProps {
  style?: Object;
  text?: string;
}

export default OfflineNotice;

TEST:

import { render } from '@testing-library/react-native';
import React from 'react';

import { useNetInfo } from '@react-native-community/netinfo';

import OfflineNotice from './offline-notice.component';
import strings from './offline-notice.strings';

describe('OfflineNotice component', () => {
  it('should display the message if internet is not reachable', () => {
    useNetInfo.mockResolvedValueOnce({
      type: 'test',
      isInternetReachable: false,
    });

    const { getByText } = render(<OfflineNotice text={strings.NO_INTERNET_CONNECTION} />);
    expect(getByText(strings.NO_INTERNET_CONNECTION)).not.toBeNull();
  });

  it('should not display the message if internet is reachable', () => {
    useNetInfo.mockResolvedValueOnce({
      type: 'test',
      isInternetReachable: true,
    });

    const { getByText } = render(<OfflineNotice text={strings.NO_INTERNET_CONNECTION} />);
    expect(getByText(strings.NO_INTERNET_CONNECTION)).toBeNull();
  });
});

Jest.Setup.ts

import mockRNCNetInfo from '@react-native-community/netinfo/jest/netinfo-mock.js';

jest.mock('@react-native-community/netinfo', () => mockRNCNetInfo);

When running the test, following output is given. What am I doing wrong here?

enter image description here

Shashika Virajh
  • 8,497
  • 17
  • 59
  • 103
0

As @Drew said, I set the configuration according by documentation, but I did a little difference in mock for testing.

For me, I did this and worked:

jest.mock('@react-native-community/netinfo', () => ({
  ...jest.requireActual('@react-native-community/netinfo'),
  useNetInfo: () => ({
    isConnected: true,
  })
}));
Rui M.
  • 31
  • 3
  • To improve the quality of your answer, consider editing to explain why you made the change, or how that improved your situation. Quality answers which help visitors learn something they can apply to their own coding are more likely to receive upvotes over time. Text also helps visitors quickly focus on key elements of your code. – SherylHohman Mar 30 '21 at 15:51