这是我的一个屏幕(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
});
});
});
