如何使用 Jest 正确测试被拒绝的承诺?

Posted

技术标签:

【中文标题】如何使用 Jest 正确测试被拒绝的承诺?【英文标题】:How do I properly test for a rejected promise using Jest? 【发布时间】:2018-11-22 03:02:46 【问题描述】:

代码

import  createUser  from '../services';
...
...

handleFormSubmit = () =>   
  this.setState( loading: true );
  createUser()
    .then(() => 
      this.setState(
        loading: false,
      );
    )
    .catch(e => 
       this.setState(
         error: e,
       );
    );
;

测试

 it('rejects...', () => 
    const Container = createUserContainer(CreateUser);
    const wrapper = shallow(<Container />);  

    return wrapper.instance().handleFormSubmit()
      .catch(e => 
         console.log("State: ", wrapper.state());
         expect(e).toEqual('error');
      );
 );

模拟

export const createUser = function() 
  return new Promise((resolve, reject) => 
    reject('error');
  );
;

测试确实强制代码进入方法中的catch。所以状态确实被设置为“错误”。

但在我的测试中,它并没有按照我的预期进行,并且在测试状态更改之前等待 Promise 拒绝。 我不确定在这里尝试什么,我应该使用 async/await 吗?

所以这是我想要等待的 createUser 方法,但我不确定我的实现是否允许这样做。

【问题讨论】:

嗨..你是怎么解决这个问题的?我有相同的代码,需要在 Promise 完成后进行测试。 仅供参考,你不应该 reject 一个字符串。你应该拒绝一个错误,比如reject(new Error('error')) 【参考方案1】:

你应该这样做:

it('rejects...', () => 
  const Container = createUserContainer(CreateUser);
  const wrapper = shallow(<Container />);  
  return expect(wrapper.instance().handleFormSubmit()).rejects.toEqual('error');
);

我认为这样更干净。你可以看到这个方法in the official docs。

【讨论】:

这是检查异步错误的官方方法。这应该是公认的答案。 注意:应该使方法异步并在期望之前添加等待(参见链接的官方文档) @Ahmed-Anas 请参阅链接中的第一个示例。以t('tests error with rejects', () =&gt; 开头的那个不是另一个。 我必须稍作改动才能完成这项工作:expect(wrapper.instance().handleFormSubmit()).rejects.toEqual(Error('error));【参考方案2】:

测试失败,因为它不知道主题是异步的。它可以通过使用done 参数或使测试函数async 来修复。

请注意,还需要设置预期断言的数量,以便即使不采用 catch 分支,测试也会失败。

async/await风格:

 it('rejects...', async () => 
    expect.assertions(1);
    const Container = createUserContainer(CreateUser);
    const wrapper = shallow(<Container />); 

    await wrapper.instance().handleFormSubmit()
      .catch(e => 
         console.log("State: ", wrapper.state());
         expect(e).toEqual('error');
      );
 );

旧式done参数:

 it('rejects...', done => 
    expect.assertions(1);
    const Container = createUserContainer(CreateUser);
    const wrapper = shallow(<Container />);  

    wrapper.instance().handleFormSubmit()
      .catch(e => 
         console.log("State: ", wrapper.state());
         expect(e).toEqual('error');
         done();
      );
 );

Asynchronous Testing Reference

expect.assertions reference

【讨论】:

这对我很有帮助。 “.catch”风格正是我所需要的。谢谢! 如果函数没有抛出,这个测试不会给出误报吗? @oyalhi 不,它不会给出误报。 expect.assertions(1) 行告诉 jest 会有一个断言,所以如果 catch 没有被触发,jest 会抱怨缺少断言【参考方案3】:

您的代码看起来正确。为什么说它不等待 Promise 拒绝?我要做的唯一不同是利用 Jest 的模拟功能,所以改变

模拟

export const createUser = function() 
  return new Promise((resolve, reject) => 
    reject('error');
  );
;

测试

jest.mock('../services');
const services = require('../services');

const createUser = jest.spyOn(services, "createUser");
createUser.mockRejectedValue("error");

...

it('rejects...', () => 

无需单独的 Mock 文件

【讨论】:

请注意,您的测试也可以确认 state.loading 仍然设置为 true 如果测试的目标代码需要使用“新服务”,因此您需要使用 requireActual 执行 jest.doMock 并仅覆盖单个函数怎么办?【参考方案4】:

在您的代码中,handleFormSubmit 函数应该返回 Promise,您可以在测试中等待。此外,您需要从成功和错误回调中返回真实数据以分别解决和拒绝承诺。

handleFormSubmit = () =>   
  this.setState( loading: true );
  return createUser()
    .then(() => 
      this.setState(
        loading: false,
      );
      return true;
    )
    .catch(e => 
       this.setState(
         error: e,
       );
       throw e;
    );
;

在您的实际代码中,您在catch 处理程序中发现了错误,并试图在测试用例代码中进一步捕获它。因此catch 不能进一步链接,而您可以链接then 多次。

参考 Promise 文档: https://www.peterbe.com/plog/chainable-catches-in-a-promise

【讨论】:

这并没有解决问题。代码仍然进入 catch,但测试仍然没有等待承诺在运行期望之前实现 我的错,已经用文档链接更新了答案。一定能解决你的问题

以上是关于如何使用 Jest 正确测试被拒绝的承诺?的主要内容,如果未能解决你的问题,请参考以下文章

开玩笑:测试超时后拒绝的承诺

如何在 Mocha 测试中调试“错误:无理由或错误理由拒绝承诺”?

用Jasmine测试被拒绝的承诺

使用 fetch 时拒绝承诺 [重复]

用 Jest 模拟基于承诺的请求

我如何测试返回承诺或拒绝的函数 - Jasmine 和 Karma