21

i want to mock useNavigation hook used inside my functional component. Any workaround how to mock it using jest?

import React from 'react';
import { useNavigation } from '@react-navigation/native';
import { TouchableOpacity } from 'react-native';

const MyComponent = () => {
  const { navigate } = useNavigation();

  const navigateToScreen = () => {
    navigate('myScreen', { param1: 'data1', param2: 'data2' })
  }

  return (<TouchableOpacity onPress={navigateToScreen}>
       Go To Screen
     </TouchableOpacity>)
}

how to test params being passed in navigate function ?

Renish Ghetia
  • 233
  • 1
  • 3
  • 6

4 Answers4

51

I know I'm late, but I had the issue where I needed to know know when the navigate function was called.

For it, I mocked the function the following way, so the mockedNavigate would be a jest function that i could use later if i needed to test if the function was actually called:

const mockedNavigate = jest.fn();

jest.mock('@react-navigation/native', () => {
  const actualNav = jest.requireActual('@react-navigation/native');
  return {
    ...actualNav,
    useNavigation: () => ({
      navigate: mockedNavigate,
    }),
  };
});

This allowed me to use this later and I would know if I could navigate properly in my application:

expect(mockedNavigate).toHaveBeenCalledTimes(1);

Hope this helps.

Brady Dowling
  • 4,920
  • 3
  • 32
  • 62
josepholiveira
  • 663
  • 6
  • 10
  • How to mock this navigation V5. with Provider? – anshuman burmman Mar 22 '21 at 07:20
  • 3
    In order for this to work I had to declare this at the root of the test file (not in a `describe` or `it/test` statement – Brady Dowling Oct 06 '21 at 17:03
  • It's also possible to put this in your jest setup file and export `mockedNavigate`. Then in each test file you only have to import that mock function, in my case: `import { mockedNavigate } from '../../../../test/setup'`. – blwinters Oct 07 '22 at 15:55
  • 1
    It's also possible to put this in your jest setup file and export `mockedNavigate`. Then in each test file you only have to import that mock function, in my case: `import { mockedNavigate } from '../../../../test/setup'`. – blwinters Oct 07 '22 at 15:55
  • Thanks @blwinters for commenting this. I've been thinking how I can access the mocked function, eventually we only need export it for access haha – Tommy Leong Oct 10 '22 at 09:48
5

Another quite late solution that is a bit cleaner than the previously suggested ones.

const mockedNavigate = jest.fn();

jest.mock('@react-navigation/native', () => (
  { useNavigation: () => ({ navigate: mockedNavigate }) }));

This does not include any other functionality of the navigation object but it will allow you to test in the following way:

expect(mockedNavigate).toHaveBeenCalledTimes(1);

Or with expect.toHaveBeenCalledWith() (here) if more applicable in your specific situation.

ThomasW
  • 344
  • 3
  • 10
4
jest.mock("@react-navigation/native", () => {
  const actualNav = jest.requireActual("@react-navigation/native")
  return {
    ...actualNav,
    useFocusEffect: () => jest.fn(),
    useNavigation: () => ({
      navigate: jest.fn(),
    }),
  }
})
Community
  • 1
  • 1
Kieran
  • 231
  • 3
  • 5
  • 2
    Can you edit this to include some explanation of what you're doing and why it's a good solution to the problem? That will help the OP and any future readers understand not just _what_ to do, but also _why_. – Jeremy Caney Jun 05 '20 at 00:30
  • it will also helpful for me if you add some example here. – anshuman burmman Mar 22 '21 at 10:46
0

This might not be a solution for a mock of useNavigation, but I found this helpful to achieve my own intent (spying), and it looks like it would achieve the OP's intent (expecting on calls)

import { CommonActions } from '@react-navigation/routers';
jest.spyOn(CommonActions, 'navigate');

This seems to be where the actions that surface out of the useNavigation hook are defined, too. I was unsuccessful to create a mock on this, but the spy works excellently.

Caveat

I'm using react-navigation 6.

I am calling render with a NavigationContainer thus I am not mocking anything in react-navigation, hence I can spy. If you don't do that, and you're looking to truly mock navigation, this won't work.

FWIW, I would recommend trying this approach first, as you can then allow much more of your code to be tested properly, as it would get used. e.g. For my CaptureLocationScreen which is in a Stack inside a modal.

render(
  <NavigationContainer>
    <RootStack.Navigator>
      <RootStack.Group screenOptions={{ presentation: 'modal', }}>
        <RootStack.Screen
          name="CaptureLocation"
          component={CaptureLocationScreen}
          options={({ navigation }: RootStackScreenProps<'CaptureLocation'>) => ({
            headerTitle: 'Add new location',
            headerLeft: () => (<CloseButton onPressed={navigation.goBack} />),
            headerRight: () => (<AcceptButton />)
          })} />
      </RootStack.Group>
    </RootStack.Navigator>
  </NavigationContainer>);

References

https://reactnavigation.org/docs/navigation-actions/