如何使用 Jest 和 Enzyme 在 React (Native) 中测试组件的父组件功能?
How to test a component is given the parent component's function in React (Native) with Jest and Enzyme?
我正在构建一个 React Native 应用程序,我正在使用 Jest 和 Enzyme 进行单元测试。此外,我正在使用 TypeScript。
我使用 Formik 构建了以下表格。
import strings from "./strings";
import styles from "./styles";
import { strings as loginStrings } from "../../screens/Login";
import { Formik, FormikActions, FormikProps } from "formik";
import React, { Component } from "react";
import { View } from "react-native";
import { Button, Input } from "react-native-elements";
import { NavigationScreenProp } from "react-navigation";
import { object as yupObject, string as yupString } from "yup";
export interface FormValues {
email: string;
password: string;
}
export interface Props {
navigation: NavigationScreenProp<any, any>;
}
export default class LoginForm extends Component<Props, object> {
handleSubmit = (values: FormValues, formikBag: FormikActions<FormValues>) => {
// ... api calls and stuff
};
renderForm = ({
values,
handleSubmit,
setFieldValue,
touched,
errors,
setFieldTouched,
isValid,
isSubmitting
}: FormikProps<FormValues>) => (
<View style={styles.container}>
// ... two inputs and a button
</View>
);
render() {
return (
<Formik
initialValues={{ email: "", password: "" }}
onSubmit={(values: FormValues, formikBag: FormikActions<FormValues>) =>
this.handleSubmit(values, formikBag)
}
validationSchema={yupObject().shape({
email: yupString()
.email(strings.invalidEmailFormat)
.required(strings.emailRequired),
password: yupString()
.min(8, strings.passwordMinLength)
.required(strings.passwordRequired)
})}
render={(formikBag: FormikProps<FormValues>) => this.renderForm(formikBag)}
/>
);
}
}
如您所见,它只是一个简单的表格。我现在想测试 <Formik />
组件是否通过了 renderForm()
和 handleSubmit
,所以我编写了以下测试:
it("should be passed the component's handleSubmit function for onSubmit", () => {
expect(wrapper.find("Formik").prop("onSubmit")).toEqual(
(values: FormValues, formikBag: FormikActions<FormValues>) =>
wrapper.instance().handleSubmit(values, formikBag)
);
});
renderForm()
也一样。不幸的是,这会引发错误:
● LoginForm › rendering › should be passed the component's handleSubmit function for onSubmit
expect(received).toEqual(expected)
Expected value to equal:
[Function anonymous]
Received:
[Function onSubmit]
Difference:
- Expected
+ Received
- [Function anonymous]
+ [Function onSubmit]
28 |
29 | it("should be passed the component's handleSubmit function for onSubmit", () => {
> 30 | expect(wrapper.find("Formik").prop("onSubmit")).toEqual(
| ^
31 | (values: FormValues, formikBag: FormikActions<FormValues>) =>
32 | wrapper.instance().handleSubmit(values, formikBag)
33 | );
所以我不确定如何才能正确测试函数是否已传递给组件。如何做到这一点?
我发现有一种方法可行,就是将函数传递给 <Formik />
,如下所示:
onSubmit={this.handleSubmit}
render={this.renderForm}
然后就是:
expect(wrapper.find("Formik").prop("onSubmit)).toEqual(wrapper.instance().onSubmit)
问题是我过去在单元测试中遇到过问题,当时我只是传入了这样的函数。同样,通过这种方式,我松开了 TypeScript 的类型。有没有办法让我的初步尝试奏效
当您定义该匿名函数时,您实际上是在创建一个新函数,因此它永远不会与渲染器内部使用的函数相同。您的选择是将其存储为对象的一部分并检查引用是否相等(这是您建议的工作):
expect(wrapper.find("Formik").prop("onSubmit)).toEqual(wrapper.instance().onSubmit)
另一种选择是模拟 handleSubmit
,并让您的测试检查当您单击 Submit
按钮时是否调用 handleSubmit
,我认为这更合理测试。
// set handleSubmit to our fake mock function
wrapper.instance().handleSubmit = jest.fn();
wrapper.update();
// Here you need to simulate a click to submit form
// I don't know how your input looks like
// but it will be similar to
inp.simulate('click')
// expect our mock function to have been called at least once
expect(wrapper.instance().handleSubmit).toHaveBeenCalledTimes(1);
我正在构建一个 React Native 应用程序,我正在使用 Jest 和 Enzyme 进行单元测试。此外,我正在使用 TypeScript。
我使用 Formik 构建了以下表格。
import strings from "./strings";
import styles from "./styles";
import { strings as loginStrings } from "../../screens/Login";
import { Formik, FormikActions, FormikProps } from "formik";
import React, { Component } from "react";
import { View } from "react-native";
import { Button, Input } from "react-native-elements";
import { NavigationScreenProp } from "react-navigation";
import { object as yupObject, string as yupString } from "yup";
export interface FormValues {
email: string;
password: string;
}
export interface Props {
navigation: NavigationScreenProp<any, any>;
}
export default class LoginForm extends Component<Props, object> {
handleSubmit = (values: FormValues, formikBag: FormikActions<FormValues>) => {
// ... api calls and stuff
};
renderForm = ({
values,
handleSubmit,
setFieldValue,
touched,
errors,
setFieldTouched,
isValid,
isSubmitting
}: FormikProps<FormValues>) => (
<View style={styles.container}>
// ... two inputs and a button
</View>
);
render() {
return (
<Formik
initialValues={{ email: "", password: "" }}
onSubmit={(values: FormValues, formikBag: FormikActions<FormValues>) =>
this.handleSubmit(values, formikBag)
}
validationSchema={yupObject().shape({
email: yupString()
.email(strings.invalidEmailFormat)
.required(strings.emailRequired),
password: yupString()
.min(8, strings.passwordMinLength)
.required(strings.passwordRequired)
})}
render={(formikBag: FormikProps<FormValues>) => this.renderForm(formikBag)}
/>
);
}
}
如您所见,它只是一个简单的表格。我现在想测试 <Formik />
组件是否通过了 renderForm()
和 handleSubmit
,所以我编写了以下测试:
it("should be passed the component's handleSubmit function for onSubmit", () => {
expect(wrapper.find("Formik").prop("onSubmit")).toEqual(
(values: FormValues, formikBag: FormikActions<FormValues>) =>
wrapper.instance().handleSubmit(values, formikBag)
);
});
renderForm()
也一样。不幸的是,这会引发错误:
● LoginForm › rendering › should be passed the component's handleSubmit function for onSubmit
expect(received).toEqual(expected)
Expected value to equal:
[Function anonymous]
Received:
[Function onSubmit]
Difference:
- Expected
+ Received
- [Function anonymous]
+ [Function onSubmit]
28 |
29 | it("should be passed the component's handleSubmit function for onSubmit", () => {
> 30 | expect(wrapper.find("Formik").prop("onSubmit")).toEqual(
| ^
31 | (values: FormValues, formikBag: FormikActions<FormValues>) =>
32 | wrapper.instance().handleSubmit(values, formikBag)
33 | );
所以我不确定如何才能正确测试函数是否已传递给组件。如何做到这一点?
我发现有一种方法可行,就是将函数传递给 <Formik />
,如下所示:
onSubmit={this.handleSubmit}
render={this.renderForm}
然后就是:
expect(wrapper.find("Formik").prop("onSubmit)).toEqual(wrapper.instance().onSubmit)
问题是我过去在单元测试中遇到过问题,当时我只是传入了这样的函数。同样,通过这种方式,我松开了 TypeScript 的类型。有没有办法让我的初步尝试奏效
当您定义该匿名函数时,您实际上是在创建一个新函数,因此它永远不会与渲染器内部使用的函数相同。您的选择是将其存储为对象的一部分并检查引用是否相等(这是您建议的工作):
expect(wrapper.find("Formik").prop("onSubmit)).toEqual(wrapper.instance().onSubmit)
另一种选择是模拟 handleSubmit
,并让您的测试检查当您单击 Submit
按钮时是否调用 handleSubmit
,我认为这更合理测试。
// set handleSubmit to our fake mock function
wrapper.instance().handleSubmit = jest.fn();
wrapper.update();
// Here you need to simulate a click to submit form
// I don't know how your input looks like
// but it will be similar to
inp.simulate('click')
// expect our mock function to have been called at least once
expect(wrapper.instance().handleSubmit).toHaveBeenCalledTimes(1);