Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Navigation: How to test components that use the withNavigation HOC?

I'm writing unit tests with Jest for my React Native app. I have a component, which is wrapped in the withNavigation HOC.

My problem is that my tests crash, throwing:

● LoginScreen component › given no props: should render a login form

    Invariant Violation: withNavigation can only be used on a view hierarchy of a navigator. The wrapped component is unable to get access to navigation from props or context.

I'm using @testing-library/react-native for my tests and I set up a custom render method like this:

import { render } from '@testing-library/react-native';
import React from 'react';
import { NavigationContext } from 'react-navigation';
import { Provider } from 'react-redux';

import store from '../src/store';

const WithProviders = ({ children }) => {
  return (
    <Provider store={store}>
      <NavigationContext.Provider>{children}</NavigationContext.Provider>
    </Provider>
  );
};

const customRender = (ui, options) =>
  render(ui, {
    wrapper: WithProviders,
    ...options,
  });

export * from '@testing-library/react-native';

export { customRender as render };

This works for the Redux context, but doesn't work for the navigation provider.

How do you test a component that is wrapped in the withNavigation HOC?

Update:

I tried the suggested answer like this:

jest.mock('react-navigation', () => ({
  withNavigation: Component => props => <Component {...props} />,
}));

afterAll(() => {
  jest.restoreAllMocks();
});

But this doesn't work. I get the error:

Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
like image 288
J. Hesters Avatar asked Aug 16 '19 13:08

J. Hesters


2 Answers

Based on Kubilay's answer, here is how I solved it:

jest.mock('react-navigation', () => ({
  withNavigation: Component => props => (
    <Component navigation={{ navigate: jest.fn() }} {...props} />
  ),
  SafeAreaView: ({ children }) => <>{children}</>,
}));

afterAll(() => {
  jest.restoreAllMocks();
});

I had to mock withNavigation as well as SafeAreaView.

This is still a very dissatisfying way. If anyone has an idea how to inject the correct React Navigation provider in the custom render method I would be super thankful.

Ideally there should be a way to configure a custom renderer that wraps the content in a container. Here is how I do it with Redux.

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

import configureStore from '../src/redux/store.js';

const store = configureStore();

const WithProviders = ({ children }) => (
  <Provider store={store}>{children}</Provider>
);

const customRender = (ui, options) =>
  render(ui, { wrapper: WithProviders, ...options });

export * from '@testing-library/react-native';

export { customRender as render };
like image 134
J. Hesters Avatar answered Sep 19 '22 09:09

J. Hesters


You should mock react-navigation, like this:

jest.mock("react-navigation", ({ withNavigation: (component) => component })

And then pass props, to your component:

const mockProps = {
    navigation: { navigate: jest.fn() }
}

wrapper = shallow(<LoginScreen {...mockProps}/>)
like image 30
Kubilay Kiymaci Avatar answered Sep 19 '22 09:09

Kubilay Kiymaci