React Native + Enzyme + Jest:模拟的redux函数调用未注册/被调用零次 [英] React Native + Enzyme + Jest: Mocked redux function call is not registered / is called zero times

查看:182
本文介绍了React Native + Enzyme + Jest:模拟的redux函数调用未注册/被调用零次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用React Native和Redux编写一个应用程序.我正在设计一个登录表单,并希望测试组件的句柄提交功能.在handleSubmit()函数中,应将几个操作分派给Redux.让我给您handleSubmit()函数代码及其测试.首先是函数本身:

I'm writing an app using React Native and Redux. I am designing a login form and want to test the components handle submit function. Within the handleSubmit() functions several actions should be dispatched to Redux. Let me give you the handleSubmit() functions code and the tests for it. First the function itself:

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);
    });
};

如您所见,我也在使用normalizr解析数据. getHouseList()getCitizenList()函数的数据在各自的函数中进行了标准化.

As you can see I'm also using normalizr to parse the data. The data of the getHouseList() and getCitizenList() functions are normalized within the respective functions.

以下是测试:

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);
  });
});

在此测试中,提供给jest-fetch-mocks(userResponsehousesResponsecitizensResponse)的值是常数.我现在这个测试失败了,因为显然没有调用应该调度Redux操作的loginSuccess()(即使我在createProps()函数中提供了jest.fn()).

In this test the values supplied to the jest-fetch-mocks (userResponse, housesResponse and citizensResponse) are constants. I now this test fails, because apparently the loginSuccess() which should dispatch a Redux action is never called (even though I supplied a jest.fn() in the createProps() function).

我做错了什么?为什么从未调用loginSuccess()函数?

What am I doing wrong? Why is the loginSuccess() function never called?

编辑:应Brian的要求,以下是api调用的代码:

Upon request from Brian, here is the code for the api call:

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;
    })
  );

推荐答案

问题

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

The assertion on props.loginSuccess() happens before the code that calls it has run.

详细信息

请务必记住,JavaScript是单线程的并且可以在消息队列之外运行(请参阅并发模型和事件循环).

It is important to remember that JavaScript is single-threaded and works off of a message queue (see Concurrency model and Event Loop).

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

It gets a message off of the queue, runs the associated function until the stack is empty, then returns to the queue to get the next message.

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

Asynchronous code in JavaScript works by adding messages to the queue.

在这种情况下,在apiLoginUser()中对then()的调用正在将消息添加到队列中,但是在beforeEach()it('should dispatch a loginSucces() action')的结尾之间,并不是所有的消息都有执行的机会.

In this case the calls to then() within apiLoginUser() are adding messages to the queue but between the end of beforeEach() and it('should dispatch a loginSucces() action') not all of them have had a chance to execute yet.

解决方案

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

The solution is to make sure the queued messages that eventually call loginSuccess() all have a chance to run before performing the assertion.

有两种可能的方法:

方法1

具有handleSubmit()返回由apiLoginUser()创建的承诺,然后在beforeEach()的末尾返回该承诺.从beforeEach()返回Promise将导致Jest 等待它解决在运行测试之前.

Have handleSubmit() return the promise created by apiLoginUser(), then return that promise at the end of beforeEach(). Returning a Promise from beforeEach() will cause Jest to wait for it to resolve before running the test.

方法2

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

Waiting on the Promise is ideal, but if the code cannot be changed then it is possible to manually delay the assertion in the test by the required number of event loop cycles. The cleanest way to do this is to make the test asynchronous and await a resolved Promise (or series of promises if more than one event loop cycle is needed):

it('should dispatch a loginSuccess() action', async () => {

  // queue the rest of the test so any pending messages in the queue can run first
  await Promise.resolve();

  expect(props.loginSuccess).toHaveBeenCalledTimes(1);

});

这篇关于React Native + Enzyme + Jest:模拟的redux函数调用未注册/被调用零次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆