【问题标题】:How to test jest.fn().mock.calls in asynchronous function如何在异步函数中测试 jest.fn().mock.calls
【发布时间】:2018-02-25 15:56:32
【问题描述】:

我正在使用 Enzyme 和 Jest 测试 React Native 组件。我已经能够测试是否调用了模拟函数(在这种情况下为 Alert.alert),如下所示:

Alert.alert = jest.fn();
someButton.simulate('Press');

expect(Alert.alert.mock.calls.length).toBe(1);

这种方法效果很好。

无论如何,我有一个登录按钮,它启动一个提取。我的 fetch 函数是这样的:

fetch(ipAddress, {
           ...
        })
            .then(response => response.json())
            .then((responseJson) => {
                if (responseJson.login === 'success') {
                    Alert.alert('Login', 'Logged in succesfully!');
                    console.log('i'm here');

我用 promise 模拟了一个 fetch。我将控制台打印添加到我的 fetch 函数中,并注意到它们打印在测试用例中,就像我预期的那样。这意味着 'i'm here' 在测试运行时打印出来。

无论如何,当我在测试用例中模拟登录按钮按下时,Alert.alert.mock.calls.length 为零。我在这里做错了什么?

【问题讨论】:

  • 您是否在问如何测试返回承诺的 fetch 调用?还是您在问为什么您的测试返回 0 而不是 1?
  • @wuno 我在问为什么在异步函数中调用模拟函数的调用计数不会更新
  • 我怀疑它不是 1 的原因是因为它还没有完成。所以它仍然是 0,因为那时它还没有返回任何东西。您似乎没有将其视为承诺。因此,当您在解释处理 Promise 的部分中查看此页面时,请在处理回调的代码部分中使用您的期望。所以使用 .then facebook.github.io/jest/docs/en/asynchronous.html
  • @wuno,如果还没有完成,console.log('i'm here') 怎么会打印在测试用例中?
  • 我不确定,但为什么不尝试使用建议的方法来处理承诺,看看是否有助于解决这个问题?我自己在测试 Promise 时遇到了很多问题。所以我理解你试图绕过你的头。您还会注意到,除非您将它们作为承诺处理,否则承诺不会返回预期的数据。如果你把它放在一个变量中并在控制台记录它,你会注意到它返回的值是不正确的。这将是一个代表承诺的长字符串。除非你使用 jest 提供的方法来解决一个承诺。

标签: javascript reactjs react-native jestjs enzyme


【解决方案1】:

我没有用 react native 来检查这一点,但我确实为 React 中的服务调用编写了一些测试(我确实使用了你没有使用的 Flux - 但这很好,在不同的地方它是相同的原理)。本质上,当您执行expect 时,承诺链尚未完成。这意味着,Alertconsole.log 都在expect 之后执行,因为默认的承诺实现将所有后续步骤置于事件队列的末尾。

解决这个问题的一种方法是使用https://www.npmjs.com/package/mock-promises - 您的规范中的beforeEach 方法需要像这样调用install

beforeEach(() => {
  Q=require('q');
  mp=require('mock-promises');
  mp.install(Q.makePromise);
  mp.reset();
  // more init code
});

别忘了

afterEach(() => {
  mp.uninstall();
});

如果您不使用 Q(我当时就这样做了),上面的链接会为您提供有关如何安装其他 Promise 的说明。

现在您有了承诺,不会将事情放在事件队列的末尾,您可以通过调用 mp.tick() 来调用下一个 then。在您的情况下,那将是

it("...", () => {
  Alert.alert = jest.fn();
  someButton.simulate('Press');
  mp.tick();
  mp.tick(); // then and then
  expect(Alert.alert.mock.calls.length).toBe(1);
});

另一种方式,开箱即用是附加另一个 thenexpects返回整个承诺。您可以在这里找到详细信息:https://facebook.github.io/jest/docs/en/tutorial-async.html

基本上,这就是它的样子:

functionReturningPromise = () => {
  // do something
  return thePromise;
}

// now testing it
it("...", () => {
  return /* !!! */ functionReturningPromise().then(() => {
    expect(/*something*/).toBeSth();
  });
});

但是,在您的情况下,这将很难,因为您无法在测试代码中处理承诺。但是,您可以将所有 fetch 逻辑拆分为一个专用方法(该方法至少返回 promise 用于测试)并为此编写一个测试。

【讨论】:

  • 感谢塞巴斯蒂安的回答!你完全正确。简单的东西是最美的。我只是将响应处理逻辑移到了单独的函数中。效果很好!
  • 更准确地说,测试该逻辑运行良好 =)
猜你喜欢
  • 2022-11-10
  • 1970-01-01
  • 2014-02-08
  • 2015-04-25
  • 2020-10-27
  • 1970-01-01
  • 2018-06-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多