【实战】react-native 测试 jest (二)

657 阅读3分钟

我们真实文件以及我的测试文件:

// screens/Details.js
import React from 'react';
import {Text, View} from 'react-native';
import Styles from '../constants/Styles';
import {useRoute} from '@react-navigation/native';

const DetailsScreen = () => {
  const {params} = useRoute();
  const {data} = params;
  return (
    <View style={Styles.container}>
      <Text style={Styles.title}>Details Screen</Text>
      <Text>Data passed: {data}</Text>
    </View>
  );
};

export default DetailsScreen;


// screens/Home.js
import React from 'react';
import {Text, TouchableOpacity, View} from 'react-native';
import {useNavigation} from '@react-navigation/native';
import Styles from '../constants/Styles';

const HomeScreen = () => {
  const {navigate} = useNavigation();
  return (
    <View style={Styles.container}>
      <Text style={Styles.title}>Home Screen</Text>
      <TouchableOpacity
        onPress={() => navigate('Details', {data: 'Potato 🥔'})}>
        <Text>Go to Details Screen</Text>
      </TouchableOpacity>
    </View>
  );
};

export default HomeScreen;
// screens/index.js
export {default as DetailsScreen} from './Details';
export {default as HomeScreen} from './Home';

// __tests__/App-test.js
import 'react-native';
import React from 'react';
import renderer, {act} from 'react-test-renderer';
import {HomeScreen} from '../screens';
import MockedNavigator from '../MockedNavigator';

it('renders correctly', async () => {
  const {toJSON} = renderer.create(<MockedNavigator component={HomeScreen} />);
  await act(async () => {
    expect(toJSON()).toMatchSnapshot();
  });
});
// MockedNavigator.js
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';

const Stack = createStackNavigator();
const MockedNavigator = ({component, params = {}}) => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen
          name="MockedScreen"
          component={component}
          initialParams={params}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

export default MockedNavigator;

这里是成功的代码。但出现的错误,下面有描述。

我在实际的项目中发现如果有用到 react-navigation 的时候就会出现很多错误,首先看第一个错误:

当什么都不做的时候 react-navigaton 的报错 我们知道在使用 react-navigation 的时候需要安装这个依赖。既然我们知道错误是那一个插件,那么查找错误就相对容易些;有两个方向,首先看这个插件或者看 react-navigation 上有没有说明。我在 react-navigation 文档中看到相关的说明 Testing with Jest 。我就按照官网说的进行添加,首先新建文件 jest.setup.js ,在里面添加如下内容:

import 'react-native-gesture-handler/jestSetup';

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

  // The mock for `call` immediately calls the callback which is incorrect
  // So we override it with a no-op
  Reanimated.default.call = () => {};

  return Reanimated;
});

// Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
jest.mock('react-native/Libraries/Animated/src/NativeAnimatedHelper');

然后在 package.json 中 jest 中添加属性 setupFiles 是一个数组,这里我们就只有一个,第一个元素的值为: "./jest.setup.js" ,这里取决于你这个文件所放的位置,我的跟 packgae.json 这个文件在同一个目录下。接着执行 yarn test ,满怀期待的眼神看着,希望能看到成功,结果却是下面的错误: 请不要怀疑人生,有第一个错误就会有无数个。

我们来分析错误,很明显这个是 react-navigation 导航的返回的图片,既然知道是 react-navigation 的错误,接下来就很简单了,首先去 react-navigation 的 github 中的 issures 中搜索,结果我看到了我想要的答案 "Syntax Error: Invalid or unexpected token" with .png #2663 ,热心的好友们提供了自己觉得可行的方案,我尝试我下面的方案,发现成功了,至于为什么目前我还不清楚。

打开 package.json 在 jest 下增加:

"moduleNameMapper": {
  "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/assetsTransformer.js",
  "\\.(css|less)$": "<rootDir>/assetsTransformer.js"
}

assetsTransformer.js 这个文件跟上面的一样新建一个,一般都不存在这个文件,有的话就直接添加,内容如下:

const path = require('path');

module.exports = {
  process(src, filename, config, options) {
    return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
  },
};

然后接着执行 yarn test

我在做的时候就在想,现在应该可以了,结果: 成功了,只不过有一个警告。

上面有提示怎么消除这个警告,按照操作来一波:

it('renders correctly', async () => {
  const {toJSON} = renderer.create(<MockedNavigator component={HomeScreen} />);
  await act(async () => {
    expect(toJSON()).toMatchSnapshot();
  });
});

这样就没有警告了,大功告成。

我要说明一下,如果没有我代码里面的 MockedNavigator.js 文件,那么就会出现下面的错误:

  ✕ renders correctly (28ms)

  ● renders correctly

    Couldn't find a navigation object. Is your component inside a screen in a navigator?

       5 |
       6 | const HomeScreen = () => {
    >  7 |   const {navigate} = useNavigation();
         |                      ^
       8 |   return (
       9 |     <View style={Styles.container}>
      10 |       <Text style={Styles.title}>Home Screen</Text>

      at useNavigation (node_modules/@react-navigation/core/lib/commonjs/useNavigation.tsx:17:11)
      at HomeScreen (screens/Home.js:7:22)
      at renderWithHooks (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5552:18)
      at mountIndeterminateComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7918:13)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9019:16)
      at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12649:12)
      at workLoopSync (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12622:22)
      at performSyncWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12333:9)
      at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1825:24
      at unstable_runWithPriority (node_modules/scheduler/cjs/scheduler.development.js:653:12)

  console.error
    The above error occurred in the <HomeScreen> component:
        in HomeScreen

    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

      at logCapturedError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:10141:21)
      at logError (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:10178:5)
      at update.callback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11288:5)
      at callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3259:12)
      at commitUpdateQueue (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3280:9)
      at commitLifeCycles (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:10497:11)
      at commitLayoutEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13295:7)

所以这个文件是需要的,至于为啥需要,目前的我也不知道。