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.
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 };
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}/>)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With