测试调用 redux-saga 函数

Test call to redux-saga function

我正在尝试测试使用 ant designredux-form 创建的表单。我创建了 containercomponent.

我使用了redux-saga作为中间件。

UserFormContainer 挂载时,它会调度操作,redux-saga 会执行操作,然后 需要 api 调用和 returns 数据。

redux-forminitialize功能填表。

问题是我想测试表格是否正在填写。但是当调用 componentdidmount() 时,我无法模拟数据获取。

UserFormContainer.js

import React, { Component } from "react";
import { connect } from "react-redux";
import UserForm from "../component/UserForm";
import { reduxForm } from "redux-form";
import actions from "../redux/actions";

class UserFormContainer extends Component {
  state = {
    loading: false
  };

  componentDidMount() {
    this.setState({
      loading: true
    });
    this.fetchUser();
  }

  fetchUser = () => {
    const { dispatch, initialize } = this.props;

    new Promise((resolve, reject) => {
      dispatch({
        type: actions.FETCH_USER,
        resolve,
        reject
      });
    })
      .then(response => {
        const {
          person: PersonEntityForm,
          contact: EmailEntityForm,
          phone: PhoneEntityForm,
          address: AddressEntityForm
        } = response;

        const userData = {
          PersonEntityForm,
          EmailEntityForm,
          PhoneEntityForm,
          AddressEntityForm
        };

        initialize(userData);
        this.setState({ loading: false });
      })
      .catch(error => console.log({ error }));
  };

  render() {
    const { loading } = this.state;
    return <UserForm loading={loading} />;
  }
}

const UserFormRedux = reduxForm({
  form: "UserForm"
})(UserFormContainer);

export default connect(null, dispatch => ({ dispatch }))(UserFormRedux);

UserFormContainer.test.js

import React from "react";
import { render, cleanup } from "@testing-library/react";
import "@testing-library/jest-dom";
import { reducer as formReducer } from "redux-form";
import { createStore, combineReducers } from "redux";
import { Provider } from "react-redux";
import UserFormContainer from "./UserFormContainer";
import userReducer from "../redux/reducers";
import { FETCH_USER } from "../redux/saga";

afterEach(cleanup);

const renderWithRedux = (
  component,
  {
    initialState,
    store = createStore(
      combineReducers({ userReducer, form: formReducer }),
      initialState
    )
  } = {}
) => {
  return {
    ...render(<Provider store={store}>{component}</Provider>)
  };
};

it("should fill form value", async () => {
  const fakeUser = {
    person: { firstName: "Don", lastName: "Joe" },
    contact: { email: "don@mail.com" },
    phone: { phone: "123456789", contactPhoneType: "personal" },
    address: {
      street: "Haven Street",
      street2: null,
      city: "Wingtown",
      state: "Wood",
      postalCode: "44600"
    }
  };

  jest.mock("../redux/saga", () => ({ FETCH_USER: jest.fn() }));
  FETCH_USER.mockImplementation(() => Promise.resolve(fakeUser));

  renderWithRedux(<UserFormContainer />);

  expect(mock).toHaveBeenCalled();
});

UserForm.js

import React, { Component } from "react";
import { FormSection } from "redux-form";
import { Form, Spin } from "antd";
import PersonalInformationForm from "./PersonalInformationForm";
import ContactEmailForm from "./ContactEmailForm";
import ContactPhoneForm from "./ContactPhoneForm";
import ContactAddressForm from "./ContactAddressForm";

class UserForm extends Component {
  render() {
    const { loading } = this.props;

    return (
      <Spin data-testid="spinning" spinning={loading}>
        <Form data-testid="userForm" style={{ width: "300px", margin: "auto" }}>
          <FormSection name="PersonEntityForm">
            <PersonalInformationForm />
          </FormSection>

          <FormSection name="EmailEntityForm">
            <ContactEmailForm />
          </FormSection>

          <FormSection name="PhoneEntityForm">
            <ContactPhoneForm />
          </FormSection>

          <FormSection name="AddressEntityForm">
            <ContactAddressForm />
          </FormSection>
        </Form>
      </Spin>
    );
  }
}

export default UserForm;

这里的问题是我正在尝试测试对 FETCH_USER 的函数调用。我的组件使用 store.dispatch 来调用函数 FETCH_USER。因此,我没有测试对 FETCH_USER 的函数调用,而是测试了 store.dispatch.

UserFormContainer.test.js

import React from "react";
import { render, cleanup } from "@testing-library/react";
import "@testing-library/jest-dom";
import { reducer as formReducer } from "redux-form";
import { createStore, combineReducers } from "redux";
import { Provider } from "react-redux";
import UserFormContainer from "./UserFormContainer";
import userReducer from "../redux/reducers";
import actions from "../redux/actions";

afterEach(cleanup);

const initialState = {};

const fakeStore = createStore(
  combineReducers({ userReducer, form: formReducer }),
  initialState
);

const renderWithRedux = (
  component,
  { initialState, store = fakeStore } = {}
) => {
  return {
    ...render(<Provider store={store}>{component}</Provider>)
  };
};

it("should fill form value", async () => {
  const fakeUser = {
    person: { firstName: "Don", lastName: "Joe" },
    contact: { email: "don@mail.com" },
    phone: { phone: "123456789", contactPhoneType: "personal" },
    address: {
      street: "Haven Street",
      street2: null,
      city: "Wingtown",
      state: "Wood",
      postalCode: "44600"
    }
  };

  fakeStore.dispatch = jest.fn();

 renderWithRedux(<UserFormContainer />);

  expect(fakeStore.dispatch).toHaveBeenCalled();
  expect(fakeStore.dispatch).toHaveBeenLastCalledWith({
    type: actions.FETCH_USER,
    resolve: expect.any(Function),
    reject: expect.any(Function)
  });
});

此外,因为我的 dispatch 函数包含在 new Promise() 中。我测试了 resolved 值,上面的代码略有变化。

const fakeUser = {
  person: { firstName: "Don", lastName: "Joe" },
  contact: { email: "don@mail.com" },
  phone: { phone: "123456789", contactPhoneType: "personal" },
  address: {
    street: "Haven Street",
    street2: null,
    city: "Wingtown",
    state: "Wood",
    postalCode: "44600"
  }
};

const fakePromise = new Promise((resolve, reject) => {
    fakeStore.dispatch = jest.fn(() => {
      try {
        resolve(fakeUser);
      } catch (error) {
        reject("error");
      }
    });
  });

  renderWithRedux(<UserFormContainer />);

  expect(fakeStore.dispatch).toHaveBeenCalled();
  expect(fakeStore.dispatch).toHaveBeenLastCalledWith({
    type: actions.FETCH_USER,
    resolve: expect.any(Function),
    reject: expect.any(Function)
  });

  expect(fakePromise).resolves.toBe(fakeUser);