8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

react-navigation v5 の useNavigation を含んだ custom hooks のテスト

Posted at

react-native で開発する際、ページ遷移を実装するのに最も適しているライブラリは react-navigation であろう(2020/06/13時点)。

react-navigation v5 では、navigation を行うために便利な useNavigationuseRoute などの custom hooks が提供されている。

useNavigation は、 <NavigationContainer> の中で存在する Component の Context から navigation を取ってきて使用可能にするための custom hooks である。

これらの hooks はとても便利である反面、component を持たない custom hooks の中で使う場合、@testing-library/react-hooks を使って custom hooks のテストする時に wrapper に navigation の context を渡す必要がある。

useNavigationuseRoute の返り値自体を mock しても良いが、params が想定通りに渡っているかどうかを確認したい場合、返り値を mock すると不便だったりする。

解決策

context を渡す一番てっとり速い方法は context を含んでいる provider でラップすることである。

なので、今回以下のようなラッパーを作成した。

MockNavigation.tsx
import * as React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import {View} from 'react-native';

const Stack = createStackNavigator();

export const MockNavigation: React.FC = ({children}) => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen
          name="MockScreen"
          component={() => <View>{children}</View>}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

@testing-library/react-hooks での使い方

以下の様なcustomHooks があるとする

useCustomHooks.ts
import {useNavigation} from '@react-navigation/native';

export const useCustomHooks = () => {
  const navigation = useNavigation()

  /**
   * navigation を含んだ処理
   */
}

以下のようにテストを書ける。
custom hooks の中で、useNavigation を使っているため、renderHook の第二引数として wrapper を提供して、上で作成した MockNavigation を使うことでテストしたい customHooks の中にも context が入る。

useCustomHooks.test.ts
import {renderHook} from '@testing-library/react-hooks';
import {MockProvider} from '__mocks__/MockProvider';
import {useCustomHooks} from '__hooks__/useCustomHooks'

describe('useCustomHooks', ()=> {
  it('works',()=>{
    const {result} = renderHook(useCustomHooks, {
      wrapper: {children} => <MockNavigation>{children}</MockNavigation>
    })
    const expected = { foo: 'bar' }
    expect(result.current).toMatchObject(expected)
  })
})

注意点

上記例では react-navigation の mock は行わなかったが、react-navigation は内部で react-native-gesture-handler と react-native-reanimated というNative層を触るライブラリを使用しているため、それらの mock は行わないと jest が落ちてしまう。

セットアップは以下のように行えば良い。

jest のセットアップ

各テストで必要なファイルをmockするために、general.js作成する。
今回は react-native-gesture-handler 及び react-native-reanimated の mock を作成する。

jest.config.js
module.exports = {
  preset: 'react-native',
  setupFiles: ['<rootDir>/__setup__/general.js'],
  transformIgnorePatterns: [
    'node_modules/(?!(jest-)?react-native|react-navigation|react-navigation-redux-helpers|@react-navigation|@react-native-community/.*)',
  ],
};

__setup__/general.js
/* eslint-env jest */

jest.mock('react-native-gesture-handler', () => {
  const View = require('react-native/Libraries/Components/View/View');
  return {
    Swipeable: View,
    DrawerLayout: View,
    State: {},
    ScrollView: View,
    Slider: View,
    Switch: View,
    TextInput: View,
    ToolbarAndroid: View,
    ViewPagerAndroid: View,
    DrawerLayoutAndroid: View,
    WebView: View,
    NativeViewGestureHandler: View,
    TapGestureHandler: View,
    FlingGestureHandler: View,
    ForceTouchGestureHandler: View,
    LongPressGestureHandler: View,
    PanGestureHandler: View,
    PinchGestureHandler: View,
    RotationGestureHandler: View,
    /* Buttons */
    RawButton: View,
    BaseButton: View,
    RectButton: View,
    BorderlessButton: View,
    /* Other */
    FlatList: View,
    gestureHandlerRootHOC: jest.fn(),
    Directions: {},
  };
});

jest.mock('react-native-reanimated', () =>
  require('react-native-reanimated/mock'),
);

まとめ

  • useNavigation を含んだ custom hooks のテストには、Navigation のラッパーが必要になる。
  • また、jest のセットアップ時に native層を触る周辺ライブラリの mock をする必要がある。

参考

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?