带有反应测试库的Formik表单提交

时间:2019-02-23 13:16:22

标签: reactjs formik react-testing-library

我希望为LoginForm触发提交处理程序。但是,由于某种原因,不是调用我的模拟函数,而是触发了组件的实际处理程序(调用外部api)。如何确保我的模拟处理程序被调用?

感兴趣的三个组件如下(演示,容器和测试套件)

LoginForm.js

import { Formik, Form, Field } from 'formik';
import { CustomInput } from '..';

const LoginForm = ({ initialValues, handleSubmit, validate }) => {
  return (
    <Formik
      initialValues={initialValues}
      validate={validate}
      onSubmit={handleSubmit}
    >
      {({ isSubmitting, handleSubmit }) => {
        return (
        <Form onSubmit={handleSubmit}>
          <div className="d-flex flex-column justify-content-center align-items-center">
            <Field
              data-testid="usernameOrEmail"
              type="text"
              name="identifier"
              placeholder="Username/Email"
              component={CustomInput}
              inputClass="mb-4 mt-2 text-monospace"
            />
            <Field
              data-testid="login-password"
              type="password"
              name="password"
              placeholder="Password"
              component={CustomInput}
              inputClass="mb-4 mt-4 text-monospace"
            />
            <button
              data-testid="login-button"
              className="btn btn-primary btn-lg mt-3 text-monospace"
              type="submit"
              disabled={isSubmitting}
              style={{ textTransform: 'uppercase', minWidth: '12rem' }}
            >
              Submit
            </button>
          </div>
        </Form>
      )}}
    </Formik>
  );
};

export default LoginForm;

LoginPage.js

import React, { useContext } from 'react';
import { loginUser } from '../../services';
import { userContext } from '../../contexts';
import { loginValidator } from '../../helpers';
import { setAuthorizationToken, renderAlert } from '../../utils';
import LoginForm from './login-form';

const INITIAL_VALUES = { identifier: '', password: '' };

const LoginPage = props => {
  const { handleUserData, handleAuthStatus } = useContext(userContext);

  const handleSubmit = async (values, { setSubmitting }) => {
    try {
      const result = await loginUser(values);
      handleAuthStatus(true);
      handleUserData(result.data);
      setAuthorizationToken(result.data.token);
      props.history.push('/habits');
      renderAlert('success', 'Login Successful');
    } catch (err) {
      renderAlert('error', err.message);
    }
    setSubmitting(false);
  };

  return (
    <LoginForm
      initialValues={INITIAL_VALUES}
      validate={values => loginValidator(values)}
      handleSubmit={handleSubmit}
    />
  );
};

export default LoginPage;

LoginPage.spec.js

import React from 'react';
import { cleanup, getByTestId, fireEvent, wait } from 'react-testing-library';
import { renderWithRouter } from '../../../helpers';
import LoginPage from '../login-page';

afterEach(cleanup);
const handleSubmit = jest.fn();

test('<LoginPage /> renders with blank fields', () => {
  const { container } = renderWithRouter(<LoginPage />);

  const usernameOrEmailNode = getByTestId(container, 'usernameOrEmail');
  const passwordNode = getByTestId(container, 'login-password');
  const submitButtonNode = getByTestId(container, 'login-button');

  expect(usernameOrEmailNode.tagName).toBe('INPUT');
  expect(passwordNode.tagName).toBe('INPUT');
  expect(submitButtonNode.tagName).toBe('BUTTON');
  expect(usernameOrEmailNode.getAttribute('value')).toBe('');
  expect(passwordNode.getAttribute('value')).toBe('');
});

test('Clicking the submit button after entering values', async () => {
  const { container } = renderWithRouter(<LoginPage handleSubmit={handleSubmit} />);

  const usernameOrEmailNode = getByTestId(container, 'usernameOrEmail');
  const passwordNode = getByTestId(container, 'login-password');
  const submitButtonNode = getByTestId(container, 'login-button');

  fireEvent.change(usernameOrEmailNode, { target: { value: fakeUser.username }});
  fireEvent.change(passwordNode, { target: { value: fakeUser.password }});
  fireEvent.click(submitButtonNode);

  await wait(() => {
    expect(handleSubmit).toHaveBeenCalledTimes(1);
  });


  expect(usernameOrEmailNode.tagName).toBe('INPUT');
  expect(passwordNode.tagName).toBe('INPUT');
  expect(submitButtonNode.tagName).toBe('BUTTON');
  expect(usernameOrEmailNode.getAttribute('value')).toBe('');
  expect(passwordNode.getAttribute('value')).toBe('');
});```

1 个答案:

答案 0 :(得分:1)

要回答您的问题,您需要首先使handleSubmit常数在LoginPage.js外部可访问,以便可以对其进行模拟然后进行测试。例如,

LoginPage.js

export const handleSubmit = async (values, { setSubmitting }) => {
 ... code to handle submission
})

在您的测试中- LoginPage.spec.js

jest.unmock('./login-page');
import LoginPage, otherFunctions from '../login-page'
otherFunctions.handleSubmit = jest.fn();

...
test('Clicking the submit button after entering values', () => {
  ...
  fireEvent.click(submitButtonNode);
  expect(handleSubmit).toHaveBeenCalledTimes(1);
})

我希望以上内容能解决您的问题。

  

但是,按照单元测试的原理,上述组件   切勿对您的操作方式进行测试。而是您的测试设置   应该是这样-

     
      
  1. 添加一个名为LoginForm.spec.js的新测试文件,以测试您的LoginForm组件。您将在此测试以下内容-      
        
    1. 检查是否所有输入字段都已呈现。
    2.   
    3. 检查是否在提交时使用正确的参数调用了正确的处理程序。
    4.   
  2.   
  3. 名为LoginPage.spec.js的现有测试文件将仅测试是否呈现了特定的表单,然后您也可以进行测试   handleSubmit方法分别做什么。
  4.   
     

我相信以上内容将使您的测试更加清晰易读   也是如此,因为关注点分离,这也将使您   测试更多边缘情况。