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

新手上路,请多包涵

我正在使用 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 navigation 道具。这是测试文件的代码(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 道具?

编辑 1: 使用 NavigationScreenProps 的方法是否正确或者我应该使用 interface 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 会抱怨组件需要一个不同类型的道具。

原文由 J. Hesters 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 534
1 个回答

问题

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

解决方案

您可以使用类型 any “选择退出类型检查并让值通过编译时检查”

细节

正如您所提到的,在 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
    });
  });
});

原文由 Brian Adams 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题