反应。弗罗米克。 Material-ui 使用 react-testing-library 的表单提交事件测试失败
React. Fromik. Material-ui form submit event testing fails using react-testing-library
我有一个使用 Formik 和 Material-ui 的 React 应用程序。
我抓住了提交按钮 html 元素,
但是 fireEvent 不起作用。
我认为问题出在 Formik 库层。
'Button' 组件是一个可重复使用的 material ui 按钮。
'change fireEvent' 测试通过。
但是我收到 'Expected mock function to have been called one time, but it was called zero times.' 消息给 'submit fireEvent'。
loginForm.test.js
import { Router, MemoryRouter } from 'react-router-dom';
import { queryByAttribute } from 'react-testing-library';
import React, { render, cleanup, fireEvent } from '../../../setupTests';
import LoginForm from '../../../../components/auth/login/loginForm';
afterEach(cleanup);
const mockSubmit = jest.fn();
const mockKeepMeLoggedIn = jest.fn();
const defaultProps = {
handleSubmit: mockSubmit,
isSubmitting: false,
userData: [],
keepMeLoggedIn: mockKeepMeLoggedIn,
};
const setUp = (props = {}) => {
const setupProps = { ...defaultProps, ...props };
const component = render(
<MemoryRouter>
<LoginForm {...setupProps} />
</MemoryRouter>,
);
const { container, getByTestId, getByText } = component;
const getByName = queryByAttribute.bind(null, 'name');
const usernameInput = getByName(container, 'username');
const passwordInput = getByName(container, 'password');
const getByType = queryByAttribute.bind(null, 'type');
const submitButton = getByType(container, 'submit');
return { component, usernameInput, passwordInput, submitButton };
};
describe('Login Form Component', () => {
it('simulate input type and click the form submit button', () => {
const { usernameInput, passwordInput, submitButton } = setUp();
fireEvent.change(usernameInput, { target: { value: 'yuch' } });
expect(usernameInput.value).toBe('yuch');
fireEvent.change(passwordInput, { target: { value: 'testpwd1234' } });
expect(passwordInput.value).toBe('testpwd1234');
fireEvent.click(submitButton);
expect(mockSubmit).toHaveBeenCalledTimes(1);
});
loginForm.js
...
import { Formik, Form } from 'formik';
/* --- Components --- */
import FormikField from '../../../shared/form/formikField';
import PasswordField from '../../../shared/form/passwordField';
import Button from '../../../shared/form/formButton';
const LoginForm = ({
keepMeLoggedIn,
keepLoggedIn,
userData,
handleSubmit,
loginValidation,
}) => {
const foundUsername = userData.length !== 0 ? userData[0].username : '';
const values = { username: foundUsername, password: '' };
return (
<Formik
initialValues={values}
validationSchema={loginValidation}
onSubmit={handleSubmit}
>
{({ isSubmitting }) => (
<div className="login-container">
<Form
className="flex flex-column-m center"
data-testid="form"
>
<FormikField
label="아이디"
name="username"
type="text"
icon="filledUser"
styleName="textField"
required
/>
...
<Button
typeValue="submit"
variantValue="contained"
buttonName="로그인"
className="login-btn"
isSubmitting={isSubmitting}
/>
</Form>
...
</div>
)}
</Formik>
);
};
button.js
import React from 'react';
import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';
const styles = theme => ({
...
});
const FormButton = ({
typeValue,
variantValue,
buttonName,
width,
isSubmitting,
classes,
className,
}) => {
...
return (
<Button
type={typeValue}
variant={variantValue}
color="primary"
size="small"
style={widthStyle}
className={`${className} ${classes.button}`}
disabled={isSubmitting}
>
{buttonName}
</Button>
);
};
我尝试过的东西。
[获取提交按钮]
const getByType = queryByAttribute.bind(null, 'type');
const submitButton = getByType(container, 'submit');
-> console.log(submitButton) // HTMLButtonElement
-> fireEvent.click(submitButton)
2.
const submitButton = getByText('로그인');
-> console.log(submitButton) // HTMLSpanElement
-> fireEvent.click(submitButton)
const submitButton = getByTestId('form');
-> console.log(submitButton) // HTMLFormElement
-> fireEvent.submit(submitButton)
[表格]
1. html 'form' 而不是 Formik 的 'Form'。
import { Formik, Form } from 'formik';
<Formik
initialValues={values}
validationSchema={loginValidation}
onSubmit={handleSubmit}
>
{({ handleSubmit, isSubmitting }) => (
<div className="login-container">
<form
className="flex flex-column-m center"
onSubmit={handleSubmit}
data-testid="form"
>
...
</form>
这实际上与 Formik 处理提交的方式有关。由于它使用的是承诺,因此在调用 onSubmit 调用之前至少需要一个滴答声。
测试库有一个 wait 实用程序,它会等待给定的时间。但是因为我们只需要等待一次报价,所以我们可以省略持续时间。
首先,从react-testing-library导入wait。然后让你的 it
函数 async
并用 wait
函数包装期望部分。
我已经通过提交按钮上的点击事件对此进行了测试。
// import wait
import { wait } from 'react-testing-library';
// add async
it('simulate input type and click the form submit button', async () => {
const { usernameInput, passwordInput, submitButton } = setUp();
fireEvent.change(usernameInput, { target: { value: 'yuch' } });
expect(usernameInput.value).toBe('yuch');
fireEvent.change(passwordInput, { target: { value: 'testpwd1234' } });
expect(passwordInput.value).toBe('testpwd1234');
fireEvent.click(submitButton);
// wrap expect in `await wait`
await wait(() => {
expect(mockSubmit).toHaveBeenCalledTimes(1);
});
});
我有一个使用 Formik 和 Material-ui 的 React 应用程序。 我抓住了提交按钮 html 元素, 但是 fireEvent 不起作用。 我认为问题出在 Formik 库层。 'Button' 组件是一个可重复使用的 material ui 按钮。 'change fireEvent' 测试通过。
但是我收到 'Expected mock function to have been called one time, but it was called zero times.' 消息给 'submit fireEvent'。
loginForm.test.js
import { Router, MemoryRouter } from 'react-router-dom';
import { queryByAttribute } from 'react-testing-library';
import React, { render, cleanup, fireEvent } from '../../../setupTests';
import LoginForm from '../../../../components/auth/login/loginForm';
afterEach(cleanup);
const mockSubmit = jest.fn();
const mockKeepMeLoggedIn = jest.fn();
const defaultProps = {
handleSubmit: mockSubmit,
isSubmitting: false,
userData: [],
keepMeLoggedIn: mockKeepMeLoggedIn,
};
const setUp = (props = {}) => {
const setupProps = { ...defaultProps, ...props };
const component = render(
<MemoryRouter>
<LoginForm {...setupProps} />
</MemoryRouter>,
);
const { container, getByTestId, getByText } = component;
const getByName = queryByAttribute.bind(null, 'name');
const usernameInput = getByName(container, 'username');
const passwordInput = getByName(container, 'password');
const getByType = queryByAttribute.bind(null, 'type');
const submitButton = getByType(container, 'submit');
return { component, usernameInput, passwordInput, submitButton };
};
describe('Login Form Component', () => {
it('simulate input type and click the form submit button', () => {
const { usernameInput, passwordInput, submitButton } = setUp();
fireEvent.change(usernameInput, { target: { value: 'yuch' } });
expect(usernameInput.value).toBe('yuch');
fireEvent.change(passwordInput, { target: { value: 'testpwd1234' } });
expect(passwordInput.value).toBe('testpwd1234');
fireEvent.click(submitButton);
expect(mockSubmit).toHaveBeenCalledTimes(1);
});
loginForm.js
...
import { Formik, Form } from 'formik';
/* --- Components --- */
import FormikField from '../../../shared/form/formikField';
import PasswordField from '../../../shared/form/passwordField';
import Button from '../../../shared/form/formButton';
const LoginForm = ({
keepMeLoggedIn,
keepLoggedIn,
userData,
handleSubmit,
loginValidation,
}) => {
const foundUsername = userData.length !== 0 ? userData[0].username : '';
const values = { username: foundUsername, password: '' };
return (
<Formik
initialValues={values}
validationSchema={loginValidation}
onSubmit={handleSubmit}
>
{({ isSubmitting }) => (
<div className="login-container">
<Form
className="flex flex-column-m center"
data-testid="form"
>
<FormikField
label="아이디"
name="username"
type="text"
icon="filledUser"
styleName="textField"
required
/>
...
<Button
typeValue="submit"
variantValue="contained"
buttonName="로그인"
className="login-btn"
isSubmitting={isSubmitting}
/>
</Form>
...
</div>
)}
</Formik>
);
};
button.js
import React from 'react';
import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';
const styles = theme => ({
...
});
const FormButton = ({
typeValue,
variantValue,
buttonName,
width,
isSubmitting,
classes,
className,
}) => {
...
return (
<Button
type={typeValue}
variant={variantValue}
color="primary"
size="small"
style={widthStyle}
className={`${className} ${classes.button}`}
disabled={isSubmitting}
>
{buttonName}
</Button>
);
};
我尝试过的东西。
[获取提交按钮]
const getByType = queryByAttribute.bind(null, 'type'); const submitButton = getByType(container, 'submit'); -> console.log(submitButton) // HTMLButtonElement -> fireEvent.click(submitButton)
2.
const submitButton = getByText('로그인');
-> console.log(submitButton) // HTMLSpanElement
-> fireEvent.click(submitButton)
const submitButton = getByTestId('form'); -> console.log(submitButton) // HTMLFormElement -> fireEvent.submit(submitButton)
[表格] 1. html 'form' 而不是 Formik 的 'Form'。
import { Formik, Form } from 'formik';
<Formik
initialValues={values}
validationSchema={loginValidation}
onSubmit={handleSubmit}
>
{({ handleSubmit, isSubmitting }) => (
<div className="login-container">
<form
className="flex flex-column-m center"
onSubmit={handleSubmit}
data-testid="form"
>
...
</form>
这实际上与 Formik 处理提交的方式有关。由于它使用的是承诺,因此在调用 onSubmit 调用之前至少需要一个滴答声。
测试库有一个 wait 实用程序,它会等待给定的时间。但是因为我们只需要等待一次报价,所以我们可以省略持续时间。
首先,从react-testing-library导入wait。然后让你的 it
函数 async
并用 wait
函数包装期望部分。
我已经通过提交按钮上的点击事件对此进行了测试。
// import wait
import { wait } from 'react-testing-library';
// add async
it('simulate input type and click the form submit button', async () => {
const { usernameInput, passwordInput, submitButton } = setUp();
fireEvent.change(usernameInput, { target: { value: 'yuch' } });
expect(usernameInput.value).toBe('yuch');
fireEvent.change(passwordInput, { target: { value: 'testpwd1234' } });
expect(passwordInput.value).toBe('testpwd1234');
fireEvent.click(submitButton);
// wrap expect in `await wait`
await wait(() => {
expect(mockSubmit).toHaveBeenCalledTimes(1);
});
});