Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react-navigation-hooks: How to test useFocusEffect

As I understand it should be done so that useFocusEffect would work as useEffect for testing (mock). I use useFocusEffect for fetchData:

useFocusEffect(
  useCallback(() => {
    fetchData();
  }, [fetchData]),
);

Error message: react-navigation hooks require a navigation context but it couldn't be found. Make sure you didn't forget to create and render the react-navigation app container. If you need to access an optional navigation object, you can useContext(NavigationContext), which may return

Package versions:

"jest": "^24.9.0",
"react-native": "0.61.2",
"react-navigation": "^4.0.10",
"react-navigation-hooks": "^1.1.0",
"@testing-library/react-native": "^4.0.14",
like image 650
Vasyl Nahuliak Avatar asked Feb 07 '20 13:02

Vasyl Nahuliak


3 Answers

For TypeScript, it is also required to satisfy type requirements as well, so in my case, it was done by using jest.requireActual:

    const withProvider = (element, store = defaultStore) => {
      // fake NavigationContext value data
      const actualNav = jest.requireActual("@react-navigation/native");
      const navContext = {
        ...actualNav.navigation,
        navigate: () => {},
        dangerouslyGetState: () => {},
        setOptions: () => {},
        addListener: () => () => {},
        isFocused: () => true,
      };
      return (
        <NavigationContext.Provider value={navContext}>
          <MyComponent />
        </NavigationContext.Provider>
      );
    };
    
    it("renders correctly", () => {
      render(withProvider(() => <SportsBooksScreen {...defaultProps} />));
    });
like image 110
Omer Gurarslan Avatar answered Sep 19 '22 11:09

Omer Gurarslan


Assuming you're rendering your component in your test, you need to wrap it in a fake <NavigationContext>. Doing so lets useFocusEffect look up the things it needs to determine if the component has been focused by your app's navigation.

This example uses render from react-native-testing-library. I think it's analogous to other rendering methods though.

import { NavigationContext } from "@react-navigation/native"
import { render } from "react-native-testing-library"

// fake NavigationContext value data
const navContext = {
  isFocused: () => true,
  // addListener returns an unscubscribe function.
  addListener: jest.fn(() => jest.fn())
}

// MyComponent needs to be inside an NavigationContext, to allow useFocusEffect to function.
const { toJSON } = render(
  <NavigationContext.Provider value={navContext}>
    <MyComponent />
  </NavigationContext.Provider>
)
like image 40
meshantz Avatar answered Sep 16 '22 11:09

meshantz


This is just a fuller version of the above answer from @meshantz.

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

// This would probably be imported from elsewhere...
const ComponentUnderTest = () => {
  useFocusEffect(
    useCallback(() => {
      fetchData();
    }, [fetchData]),
  );
  
  return null;
};

const mockFetchData = jest.fn();
jest.mock('fetchData', () => mockFetchData);

describe('testing useFocusOnEffect in ComponentUnderTest', () => {
  afterAll(() => {
    jest.restoreAllMocks();
  });

  describe('when the view comes into focus', () => {
    it('calls fetchData', () => {
      const navContextValue = {
        isFocused: () => false,
        addListener: jest.fn(() => jest.fn()),
      };

      render(
        <NavigationContext.Provider value={navContextValue}>
          <ComponentUnderTest />
        </NavigationContext.Provider>,
      );

      expect(mockFetchData).toBeCalledTimes(0);

      render(
        <NavigationContext.Provider
          value={{
            ...navContextValue,
            isFocused: () => true,
          }}
        >
          <ComponentUnderTest />
        </NavigationContext.Provider>,
      );

      expect(mockFetchData).toBeCalledTimes(1);
    });
  });
});

like image 42
ourmaninamsterdam Avatar answered Sep 16 '22 11:09

ourmaninamsterdam