reactjs - React Native 0.56 + Enzyme + Jest + React Navigation:Enzyme 因 import 语句而崩溃
问题描述
TL;DR:由于以下与 React Navigation 相关的错误,我的测试崩溃了:
/path-to-app/react-native/myApp/node_modules/react-navigation/src/views/withNavigation.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import React from 'react';
^^^^^^
SyntaxError: Unexpected token import
127 | }
128 |
> 129 | export default withNavigation(
| ^
130 | connect(
131 | null,
132 | { loginSuccess }
at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)
at Object.get withNavigation [as withNavigation] (node_modules/react-navigation/src/react-navigation.js:166:12)
at Object.<anonymous> (app/components/LoginForm/LoginForm.js:129:32)
奇怪的是我使用了一个命名导出,这就是为什么甚至不应该加载 React Navigation。
我正在尝试为我的登录表单编写单元测试。这是表单的代码:
import React, { Component } from "react";
import { View } from "react-native";
import { connect } from "react-redux";
import { Formik } from "formik";
import { object as yupObject, string as yupString } from "yup";
import { withNavigation } from "react-navigation";
import PropTypes from "prop-types";
import { loginSuccess } from "../../actions/login/login";
import alert from "../../api/alert/alert";
import { apiLoginUser } from "../../api/auth/auth";
import {
BUTTON_TEXT_LOGIN,
BUTTON_TEXT_FORGOTTEN_PASSWORD
} from "../../config/constants/buttonTexts";
import {
ERROR_MESSAGE_INVALID_EMAIL_FORMAT,
ERROR_MESSAGE_EMAIL_REQUIRED,
ERROR_MESSAGE_PASSWORD_REQUIRED,
ERROR_MESSAGE_PASSWORD_MIN_LENGTH
} from "../../config/constants/errorMessages";
import AuthInput from "../AuthInput/AuthInput";
import Button from "../Button/Button";
import ClearButton from "../ClearButton/ClearButton";
import styles from "./styles";
export class LoginForm extends Component {
static propTypes = {
navigation: PropTypes.object,
loginSuccess: PropTypes.func,
isSubmitting: PropTypes.bool
};
handleSubmit = (values, formikBag) => {
formikBag.setSubmitting(true);
apiLoginUser(values.email, values.password)
.then(data => {
this.props.navigation.navigate("HomeScreen");
formikBag.setSubmitting(false);
this.props.loginSuccess(data.user);
})
.catch(error => {
alert(error);
formikBag.setSubmitting(false);
});
};
renderForm = (
values,
handleSubmit,
setFieldValue,
errors,
touched,
setFieldTouched,
isValid,
isSubmitting
) => (
<View style={styles.inputContainer}>
<AuthInput
placeholder="Email address"
value={values.email}
onChange={setFieldValue}
onTouch={setFieldTouched}
name="email"
error={touched.email && errors.email}
editable={!isSubmitting}
/>
<AuthInput
placeholder="Password"
value={values.password}
onChange={setFieldValue}
onTouch={setFieldTouched}
name="password"
error={touched.password && errors.password}
editable={!isSubmitting}
secureTextEntry
/>
<ClearButton
text={BUTTON_TEXT_FORGOTTEN_PASSWORD}
onPress={() => {}}
containerStyles={styles.clearButtonContainer}
buttonTextStyles={styles.clearButtonText}
/>
<Button onPress={handleSubmit} disabled={!isValid || isSubmitting} loading={isSubmitting}>
{BUTTON_TEXT_LOGIN}
</Button>
</View>
);
render() {
return (
<Formik
initialValues={{ email: "", password: "" }}
onSubmit={this.handleSubmit}
validationSchema={yupObject().shape({
email: yupString()
.email(ERROR_MESSAGE_INVALID_EMAIL_FORMAT)
.required(ERROR_MESSAGE_EMAIL_REQUIRED),
password: yupString()
.min(12, ERROR_MESSAGE_PASSWORD_MIN_LENGTH)
.required(ERROR_MESSAGE_PASSWORD_REQUIRED)
})}
render={({
values,
handleSubmit,
setFieldValue,
errors,
touched,
setFieldTouched,
isValid,
isSubmitting
}) =>
this.renderForm(
values,
handleSubmit,
setFieldValue,
errors,
touched,
setFieldTouched,
isValid,
isSubmitting
)
}
/>
);
}
}
export default withNavigation(
connect(
null,
{ loginSuccess }
)(LoginForm)
);
这是我的测试文件:
import React from "react";
import { View } from "react-native";
import { shallow } from "enzyme";
import { LoginForm } from "./LoginForm";
describe("LoginForm", () => {
describe("rendering", () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<LoginForm />);
});
it("should render a <View />", () => {
expect(wrapper.find(View)).toHaveLength(1);
});
});
});
如您所见,我使用命名导LoginForm
出来拆分ConnectedLoginForm
连接到 Redux 和 React Navigation 的导出。还有一部分 Enzyme 或 Jest 不喜欢 React Navigation。你知道如何规避这个问题吗?
编辑 1
我设法使用 NavigationService 找到了解决方法。知道如何解决这个问题仍然很棒,因为这个错误还阻止我测试使用 React Navigation 的屏幕。
编辑 2
对于任何想知道如何模拟这个的人,我最终做的是创建一个__mocks__
与文件夹相邻的node_modules
文件夹。在其中我创建了一个名为的文件react-navigation.js
,我在其中模拟了我需要模拟的所有行为。
在这种情况下,withNavigation()
这意味着只实现一个虚拟HOC:
export const withNavigation = () => WrappedComponent => WrappedComponent;
解决方案
您可以像这样模拟依赖项,例如导航:
jest.mock('react-navigation', () => ({ withNavigation: component => component}));
然后手动将道具(包括导航)传递给您的组件:
const mockProps = {
navigation: { navigate: jest.fn() },
loginSuccess: jest.fn(),
isSubmitting: true
}
wrapper = shallow(<LoginForm {...mockProps}/>);
推荐阅读
- java - 必需: GeneratedMessageV3.Builder<*>:如何使用值初始化 com.google.type.Money
- entity-framework-core - 从 Entity 实例创建代理
- assembly - 在 ARMv7 上使用 NEON 进行 SIMD 无符号 64 位比较 (CMHS) 的最有效方法是什么?
- flutter - 如何通过颤振更改系统DNS?
- c++ - 简化外部函数调用
- ruby-on-rails - 如何在rails中存储特定用户选择的类别和子类别
- google-bigquery - BigQuery 排序规则
- git - Git revert 无法恢复已删除的文件
- php - 从复杂的 php 数组中打印表格或列表
- reactjs - React,调用另一个组件中的函数