React native React Native+Ezyme+Jest:模拟redux函数调用未注册/调用了零次

React native React Native+Ezyme+Jest:模拟redux函数调用未注册/调用了零次,react-native,mocking,jestjs,enzyme,jest-fetch-mock,React Native,Mocking,Jestjs,Enzyme,Jest Fetch Mock,我正在使用React Native和Redux编写一个应用程序。我正在设计一个登录表单,希望测试组件句柄提交功能。在handleSubmit函数中,应该向Redux分派几个操作。让我给你一些简单的submit函数代码和测试。首先,函数本身: handleSubmit = (values, formikBag) => { formikBag.setSubmitting(true); const { loginSuccess, navigation, setHouses, setCit

我正在使用React Native和Redux编写一个应用程序。我正在设计一个登录表单,希望测试组件句柄提交功能。在handleSubmit函数中,应该向Redux分派几个操作。让我给你一些简单的submit函数代码和测试。首先,函数本身:

handleSubmit = (values, formikBag) => {
  formikBag.setSubmitting(true);
  const { loginSuccess, navigation, setHouses, setCitizens } = this.props;
  apiLoginUser(values.email, values.password)
    .then(data => {
      const camelizedJson = camelizeKeys(data.user);
      const normalizedData = Object.assign({}, normalize(camelizedJson, userSchema));
      loginSuccess(normalizedData);

      const tokenPromise = setToken(data.key);
      const housePromise = getHouseList();
      Promise.all([tokenPromise, housePromise])
        .then(values => {
          setHouses(values[1]);
          getCitizenList(values[1].result[0])
            .then(citizens => {
              setCitizens(citizens);
              formikBag.setSubmitting(false);
              navigation.navigate("HomeScreen");
            })
            .catch(err => {
              formikBag.setSubmitting(false);
              alert(err);
            });
        })
        .catch(err => {
          console.log(err);
          formikBag.setSubmitting(false);
          alert(err);
        });
    })
    .catch(error => {
      alert(error);
      formikBag.setSubmitting(false);
    });
};
如您所见,我还使用normalizer解析数据。getHouseList和getCitizenList函数的数据在各自的函数中被规范化

以下是测试:

const createTestProps = props => ({
  navigation: { navigate: jest.fn() },
  loginSuccess: jest.fn(),
  setHouses: jest.fn(),
  setCitizens: jest.fn(),
  ...props
});

...

describe("component methods", () => {
  let wrapper;
  let props;
  beforeEach(() => {
    props = createTestProps();
    wrapper = shallow(<LoginForm {...props} />);
    fetch.mockResponseOnce(JSON.stringify(userResponse));
    fetch.mockResponseOnce(JSON.stringify(housesResponse));
    fetch.mockResponseOnce(JSON.stringify(citizensResponse));
    wrapper
      .instance()
      .handleSubmit({ email: "abc", password: "def" }, { setSubmitting: jest.fn() });
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  it("should dispatch a loginSuccess() action", () => {
    expect(props.loginSuccess).toHaveBeenCalledTimes(1);
  });
});
发行

props.loginsAccess上的断言发生在调用它的代码运行之前

细节

记住JavaScript是单线程的,并且在消息队列之外工作,这一点很重要,请参见

它从队列中获取一条消息,运行相关函数直到堆栈为空,然后返回队列以获取下一条消息

JavaScript中的异步代码通过向队列中添加消息来工作

在这种情况下,在apiLoginUser中对then的调用将消息添加到队列中,但在beforeEach结束和它“应该分派LoginSAccess操作”之间,并非所有调用都有机会执行

解决方案

解决方案是确保最终调用LoginAccess的排队消息在执行断言之前都有机会运行

有两种可能的方法:

方法1

让handleSubmit返回apiLoginUser创建的承诺,然后在每次结束前返回该承诺。从以前的承诺中回馈会引起笑话

方法2

等待承诺是理想的,但如果代码无法更改,则可以手动将测试中的断言延迟所需的事件循环次数。最干净的方法是使测试异步,如果需要一个以上的事件循环周期,则等待已解决的承诺或一系列承诺:

它“应该发送一个loginsAccess操作”,异步=>{ //将测试的其余部分排队,以便队列中的任何挂起消息都可以首先运行 等待承诺。决心; expectprops.LoginsAccess.To已催收时间1; };
你确定你贴在问题上的所有代码都与你的问题有关吗?把问题尽量简短会增加回答这个问题的动机。@Moyote老实说,我很不确定。问题是,由于我无法找出问题的确切位置,我想提供尽可能多的信息。我不知道如何用精确的短句来描述这个问题,代码比千言万语更能说明问题;我理解。但是,如果代码比千言万语更响亮,那么对于可能的助手来说,可能现在太吵了:@J.Hester你能为apiLoginUser分享代码吗?@brian住在户外当然我在问题中编辑过!:很乐意帮忙
export const apiLoginUser = (email, password) =>
  postRequestWithoutHeader(ROUTE_LOGIN, { email: email, password: password });

export const postRequestWithoutHeader = (fullUrlRoute, body) =>
  fetch(fullUrlRoute, {
    method: "POST",
    body: JSON.stringify(body),
    headers: { "Content-Type": "application/json" }
  }).then(response =>
    response.json().then(json => {
      if (!response.ok) {
        return Promise.reject(json);
      }
      return json;
    })
  );