我正在测试一个 Vue 组件,当某个参数出现在路由中时,它会调用我的 Vuex 存储中的某个操作。我正在使用 jest.fn()
来模拟这个 Action 。
这里是组件的相关代码:
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()
失败了。
这怎么可能?它适用于我模拟的另一个功能。替换我模拟函数的顺序并不重要。删除另一个函数被调用的期望也无关紧要,因此它看起来不像是冲突。
最佳答案
How is this possible?
expect
在 selectElement
有机会运行之前运行并失败。
详情
消息队列
JavaScript 使用 message queue .当前留言runs to completion在下一个开始之前。
PromiseJobs 队列
ES6 引入了 PromiseJobs queue它处理“对 promise 的解决作出回应”的工作。 PromiseJobs 队列中的任何作业都在当前消息完成后和下一条消息开始之前运行。
异步/等待
async
和 await
只是 syntactic sugar over promises and generators .在 Promise
上调用 await
实质上将函数的其余部分包装在回调中,以便在 Promise
解析时在 PromiseJobs 中安排。
会发生什么
您的测试作为当前正在运行的消息开始运行。调用 shallowMount
加载您的组件,该组件一直运行到 await this.$store.dispatch('someOtherAction');
调用 someOtherFunction
然后基本上将函数的其余部分作为 Promise
回调在 Promise
解析时安排在 PromiseJobs 队列中。
然后执行返回到运行两个 expect
语句的测试。第一个通过,因为 someOtherFunction
已被调用,但第二个失败,因为 selectElement
尚未运行。
然后当前正在运行的消息完成,然后运行 PromiseJobs 队列中的待处理作业。调用 selectElement
的回调在队列中,因此它运行并调用记录到控制台的 selectElement
。
解决方案
确保调用 selectElement
的 Promise
回调在运行 expect
之前已经运行。
只要有可能,最好返回 Promise
,这样测试就可以直接await
。
如果那是不可能的,那么解决方法是在测试期间对已解决的 Promise
调用 await
,这实际上将其余测试排在 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
});
关于vue.js - jest.fn() 声称没有被调用,但是,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54890916/