如何使用 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', () =>
开头的那个不是另一个。
我必须稍作改动才能完成这项工作: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 正确测试被拒绝的承诺?的主要内容,如果未能解决你的问题,请参考以下文章