Renish Ghetia
Renish Ghetia

Reputation: 273

How to mock useNavigation hook in react-navigation 5.0 for jest test?

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 ?

Upvotes: 25

Views: 27000

Answers (4)

Sean P
Sean P

Reputation: 1

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/

Upvotes: 0

josepholiveira
josepholiveira

Reputation: 743

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.

Upvotes: 57

ThomasW
ThomasW

Reputation: 374

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.

Upvotes: 6

Kieran
Kieran

Reputation: 271

jest.mock("@react-navigation/native", () => {
  const actualNav = jest.requireActual("@react-navigation/native")
  return {
    ...actualNav,
    useFocusEffect: () => jest.fn(),
    useNavigation: () => ({
      navigate: jest.fn(),
    }),
  }
})

Upvotes: 5

Related Questions