jest.fn()声称没有被调用,但有

问题描述 投票:0回答:1

我正在测试一个Vue组件,当路由中存在某个参数时,该组件会在我的Vuex存储中调用某个操作。我正在用jest.fn()嘲笑这个动作。

这是组件的相关代码:

await this.$store.dispatch('someOtherAction');
if (this.$route.params && this.$route.params.id) {
    this.$store.dispatch('selection/selectElement', parseInt(this.$route.params.id, 10));
}

这是模拟功能:

someOtherAction = jest.fn();
selectElement = jest.fn(() => console.log("selectElement has been called"));

我的测试:

it('selects element if passed in route', async () => {
  const $route = {params: {id: '256'}};
  const wrapper = shallowMount(AbcModel, {
    mocks: {$route},
    store, localVue
  });
  expect(someOtherAction).toHaveBeenCalled();
  expect(selectElement).toHaveBeenCalled();
});

在输出中,我可以看到'selectElement已被调用'。显然它被称为。然而,expect(selectElement).toHaveBeenCalled()失败了。

这怎么可能?它与我嘲笑的另一个函数一起工作正常。替换我模拟函数的顺序无关紧要。删除其他函数被调用的期望也无关紧要,因此它看起来不像是碰撞。

vue.js vuejs2 jestjs vuex
1个回答
3
投票

这怎么可能?

expect有机会跑之前,selectElement跑了并且失败了。


细节

消息队列

JavaScript使用message queue。当前消息runs to completion在下一个消息开始之前。

PromiseJobs队列

ES6引入了PromiseJobs queue,它处理的工作“是对Promise的解决”。 PromiseJobs队列中的所有作业在当前消息完成之后和下一条消息开始之前运行。

异步/等待

asyncawait只是syntactic sugar over promises and generators。在await解析时,在Promise上调用Promise实际上将函数的其余部分包装在PromiseJobs中的调用中。

怎么了

您的测试开始作为当前运行的消息运行。调用shallowMount加载你的组件,该组件一直运行到调用await this.$store.dispatch('someOtherAction');someOtherFunction,然后基本上将函数的其余部分排队为Promise回调,以便在Promise结算时在PromiseJobs队列中进行调度。

执行然后返回到运行两个expect语句的测试。自someOtherFunction被召唤以来,第一个过去了,但是第二个失败,因为selectElement尚未运行。

然后,当前运行的消息完成,然后运行PromiseJobs队列中的挂起作业。调用selectElement的回调在队列中,因此它运行并调用selectElement,它会记录到控制台。


确保在运行Promise之前调用selectElementexpect回调已经运行。

只要有可能,它是理想的返回Promise所以测试可以直接await它。

如果这是不可能的话,那么解决方法是在测试期间在已解析的await上调用Promise,它基本上将其余的测试排在PromiseJobs队列的后面,并允许任何挂起的Promise回调首先运行:

it('selects element if passed in route', async () => {
  const $route = {params: {id: '256'}};
  const wrapper = shallowMount(AbcModel, {
    mocks: {$route},
    store, localVue
  });
  expect(someOtherFunction).toHaveBeenCalled();
  // Ideally await the Promise directly...
  // but if that isn't possible then calling await Promise.resolve()
  // queues the rest of the test at the back of PromiseJobs
  // allowing any pending callbacks to run first
  await Promise.resolve();
  expect(selectElement).toHaveBeenCalled();  // SUCCESS
});
© www.soinside.com 2019 - 2024. All rights reserved.