3

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

下面是三个感兴趣的组件(演示、容器和测试套件)

登录表单.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;

登录页面.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;

登录页面.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('');
});```

4

1 回答 1

2

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

登录页面.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. 检查是否在提交时调用了正确的处理程序并使用正确的参数。
  2. 然后,调用的现有测试文件LoginPage.spec.js将仅测试特定表单是否已呈现,然后您还可以测试该handleSubmit方法单独执行的操作。

我相信由于关注点的分离,上述内容也会使您的测试更加清晰和可读,并且还可以让您测试更多的边缘案例。

于 2019-02-24T06:52:33.773 回答