首页 > 解决方案 > 做出反应。弗罗米克。Material-ui 表单提交事件测试失败使用 react-testing-library

问题描述

我有一个使用 Formik 和 Material-ui 的反应应用程序。我抓住了提交按钮 html 元素,但是 fireEvent 不起作用。我认为问题来自 Formik 库层。'Button' 组件是一个可重复使用的材质 ui 按钮。'change fireEvent' 测试通过。

但是我收到“预期的模拟函数已被调用一次,但它被调用了零次。” '提交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>
  );
};

按钮.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>
  );
};

我尝试过的事情。

[获取提交按钮]

  1. const getByType = queryByAttribute.bind(null, 'type');
    
    const submitButton = getByType(container, 'submit');
    
    -> console.log(submitButton) // HTMLButtonElement
    
    -> fireEvent.click(submitButton)
    
  2. 2.
const submitButton = getByText('로그인');

-> console.log(submitButton) // HTMLSpanElement

-> fireEvent.click(submitButton)
  1. const submitButton = getByTestId('form');
    
    -> console.log(submitButton) // HTMLFormElement
    
    -> fireEvent.submit(submitButton)
    

[ form ] 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>

标签: reactjsjestjsmaterial-uiformikreact-testing-library

解决方案


它实际上与 Formik 如何处理提交有关。由于它使用的是 Promise,因此在调用 onSubmit 调用之前至少需要一个滴答声。

测试库有一个等待给定时间的实用程序。但是由于我们只需要等待一个滴答声,我们可以省略持续时间。

首先,从 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);
  });
});

推荐阅读