当前位置 : 主页 > 网页制作 > React >

如何使用React Native中的TypeScript模拟React Navigation的单元测试导航道具?

来源:互联网 收集:自由互联 发布时间:2021-06-15
我正在用TypeScript构建一个React Native应用程序.对于我的导航,我使用React Navigation,对于我的单元测试,我使用Jest和Enzyme. 这是我的一个屏幕(LoadingScreen.tsx)的(精简的)代码: import styles from "
我正在用TypeScript构建一个React Native应用程序.对于我的导航,我使用React Navigation,对于我的单元测试,我使用Jest和Enzyme.

这是我的一个屏幕(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导航道具.这是测试文件的代码(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需要导航道具.

我收到错误:

[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

我怎样才能解决这个问题?

我想我不得不嘲笑导航道具.我试过这样做(你可以看到我在我的测试中从React Navigation导入*),但是无法弄明白.只有NavigationActions是远程有用的,但它只包含navigate(). TypeScript期望模拟所有内容,甚至是状态.我怎么能嘲笑导航道具呢?

编辑1:使用NavigationScreenProps的方法是否正确或我应该使用接口Props方法?如果是,你会如何模拟(它导致相同的错误).

编辑2:
使用第二种方法与接口和

export class LoadingScreen extends Component<Props, object>

我能够“解决”这个问题.我真的必须像这样模拟导航对象的每个属性:

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
});

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

编辑3:
当我使用JS时,只需要模拟我需要的属性(通常只是导航)就足够了.但是自从我开始使用TypeScript以来,我不得不模仿导航的每个方面.否则TypeScript会抱怨该组件需要一个具有不同类型的道具.

问题

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

您可以使用任何类型“to opt-out of type-checking and let the values pass through compile-time checks”.

细节

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

在TypeScript中,相同的mock会导致错误,因为它与预期的类型不完全匹配.

在这种情况下,如果你有一个你知道的模拟与预期的类型不匹配,你可以使用any来允许模拟通过编译时检查.

这是一个更新的测试:

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
    });
  });
});
网友评论