如何在React Native中使用TypeScript模拟React Navigation的导航测试以进行单元测试? [英] How to mock React Navigation's navigation prop for unit tests with TypeScript in React Native?

查看:59
本文介绍了如何在React Native中使用TypeScript模拟React Navigation的导航测试以进行单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用TypeScript构建一个React Native应用程序.对于我的导航,我使用React Navigation,对于我的单元测试,我使用Jest和Enzyme.

I'm building a React Native app with TypeScript. For my navigation I use React Navigation and for my unit testing I use Jest and Enzyme.

这是我的其中一个屏幕(LoadingScreen.tsx)的(精简代码):

Here is the (stripped down) code for one of my screen (LoadingScreen.tsx):

import styles from "./styles";
import React, { Component } from "react";
import { Text, View } from "react-native";
import { NavigationScreenProps } from "react-navigation";

// Is this correct?
export class LoadingScreen extends Component<NavigationScreenProps> {
// Or should I've done:
// export interface Props {
//   navigation: NavigationScreenProp<any, any>;
// }

// export class LoadingScreen extends Component<Props> {
  componentDidMount = () => {
    this.props.navigation.navigate("LoginScreen");
  };

  render() {
    return (
      <View style={styles.container}>
        <Text>This is the LoadingScreen.</Text>
      </View>
    );
  }
}

export default LoadingScreen;

当尝试测试屏幕时,我遇到了一个问题.屏幕需要一个类型为NavigiationScreenProps的道具,因为我正在访问React Navigations navigation道具.这是测试文件的代码(LoadingScreen.test.tsx):

When trying to test the screens I came across a problem. The screens expects a prop with a type of NavigiationScreenProps because I'm accessing React Navigations navigation prop. Here is the testing file's code (LoadingScreen.test.tsx):

import { LoadingScreen } from "./LoadingScreen";
import { shallow, ShallowWrapper } from "enzyme";
import React from "react";
import { View } from "react-native";
import * as navigation from "react-navigation";

const createTestProps = (props: Object) => ({
  ...props
});

describe("LoadingScreen", () => {
  describe("rendering", () => {
    let wrapper: ShallowWrapper;
    let props: Object;
    beforeEach(() => {
      props = createTestProps({});
      wrapper = shallow(<LoadingScreen {...props} />);
    });

    it("should render a <View />", () => {
      expect(wrapper.find(View)).toHaveLength(1);
    });
  });
});

问题是LoadingScreen需要一个navigation道具.

我得到了错误:

[ts]
Type '{ constructor: Function; toString(): string; toLocaleString(): string; valueOf(): Object; hasOwnProperty(v: string | number | symbol): boolean; isPrototypeOf(v: Object): boolean; propertyIsEnumerable(v: string | ... 1 more ... | symbol): boolean; }' is not assignable to type 'Readonly<NavigationScreenProps<NavigationParams, any>>'.
  Property 'navigation' is missing in type '{ constructor: Function; toString(): string; toLocaleString(): string; valueOf(): Object; hasOwnProperty(v: string | number | symbol): boolean; isPrototypeOf(v: Object): boolean; propertyIsEnumerable(v: string | ... 1 more ... | symbol): boolean; }'.
(alias) class LoadingScreen

我该如何解决?

我认为我必须以某种方式模拟navigation道具.我尝试这样做(如您所见,我在测试中从React Navigation导入了*),但无法弄清楚.只有NavigationActions对远程有用,但仅包括navigate(). TypeScript期望所有东西,包括状态,都可以被模拟.如何模拟navigation道具?

I think I somehow have to mock the navigation prop. I tried doing that (as you can see I imported * from React Navigation in my test), but couldn't figure out. There is only NavigationActions that is remotely useful but it only includes navigate(). TypeScript expects everything, even the state, to be mocked. How can I mock the navigation prop?

使用NavigationScreenProps的方法是否正确,还是我应该使用interface Props的方法?如果是,那么您将如何模拟(导致相同的错误).

Edit 1: Is the approach of using NavigationScreenProps even correct or should I use the interface Props approach? If yes how would you then mock than (it results in the same error).

修改2: 通过接口和

export class LoadingScreen extends Component<Props, object>

我能够解决"此问题.我真的不得不像这样模拟导航对象的每个属性:

I was able to "solve" this problem. I literally had to mock every single property of the navigation object like this:

const createTestProps = (props: Object) => ({
  navigation: {
    state: { params: {} },
    dispatch: jest.fn(),
    goBack: jest.fn(),
    dismiss: jest.fn(),
    navigate: jest.fn(),
    openDrawer: jest.fn(),
    closeDrawer: jest.fn(),
    toggleDrawer: jest.fn(),
    getParam: jest.fn(),
    setParams: jest.fn(),
    addListener: jest.fn(),
    push: jest.fn(),
    replace: jest.fn(),
    pop: jest.fn(),
    popToTop: jest.fn(),
    isFocused: jest.fn()
  },
  ...props
});

问题仍然存在:这是正确的吗?还是有更好的解决方案?

The question remains: Is this correct? Or is there a better solution?

修改3: 回到我使用JS时,仅模拟我需要的属性就足够了(通常只是导航).但是自从我开始使用TypeScript以来,我不得不模拟导航的各个方面.否则,TypeScript会抱怨组件期望使用其他类型的道具.

Edit 3: Back when I used JS, it was enough to mock only the property I needed (often just navigate). But since I started using TypeScript, I had to mock every single aspects of navigation. Otherwise TypeScript would complain that the component expects a prop with a different type.

推荐答案

问题

该模拟与预期的类型不匹配,因此TypeScript报告错误.

Issue

The mock does not match the expected type so TypeScript reports an error.

您可以使用any类型退出类型检查,然后让值通过编译时检查"..

正如您提到的,在JavaScript中,它只能模拟测试所需的内容.

As you mentioned, in JavaScript it works to mock only what is needed for the test.

在TypeScript中,同一模拟程序会导致错误,因为它与所需的类型不完全匹配.

In TypeScript the same mock will cause an error since it does not completely match the expected type.

在这种情况下,如果您知道某个模拟不符合预期的类型,则可以使用any允许该模拟通过编译时检查.

In situations like these where you have a mock that you know does not match the expected type you can use any to allow the mock to pass through compile-time checks.

这是更新的测试:

import { LoadingScreen } from "./LoadingScreen";
import { shallow, ShallowWrapper } from "enzyme";
import React from "react";
import { View } from "react-native";

const createTestProps = (props: Object) => ({
  navigation: {
    navigate: jest.fn()
  },
  ...props
});

describe("LoadingScreen", () => {
  describe("rendering", () => {
    let wrapper: ShallowWrapper;
    let props: any;   // use type "any" to opt-out of type-checking
    beforeEach(() => {
      props = createTestProps({});
      wrapper = shallow(<LoadingScreen {...props} />);   // no compile-time error
    });

    it("should render a <View />", () => {
      expect(wrapper.find(View)).toHaveLength(1);   // SUCCESS
      expect(props.navigation.navigate).toHaveBeenCalledWith('LoginScreen');   // SUCCESS
    });
  });
});

这篇关于如何在React Native中使用TypeScript模拟React Navigation的导航测试以进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆