【问题标题】:How do I properly test for a rejected promise using Jest?如何使用 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'))

标签: reactjs promise mocking jestjs enzyme


【解决方案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,但测试仍然没有等待承诺在运行期望之前实现
  • 我的错,已经用文档链接更新了答案。一定能解决你的问题
猜你喜欢
  • 2020-07-06
  • 2022-01-23
  • 2018-02-25
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 2017-07-16
  • 2015-06-04
  • 1970-01-01
相关资源
最近更新 更多