Vue组件测试:如何用Jest单断言验证dispatch两次调用同参数
问题
我正在测试一个Vue组件,该组件在挂载时会dispatch一个action(functionx),点击特定刷新按钮时也会dispatch同一个action。我当前的测试代码如下:
it('makes an api call to server when refresh button is clicked', () => { store.dispatch = jest.fn(); const wrapper = mount(Categories, { sync: false, store, localVue }); const refreshButton = wrapper.find('span.fa-sync'); refreshButton.trigger('click'); expect(store.dispatch).toHaveBeenCalledWith('categoryStore/fetchCategories'); })
该测试可正常通过,但由于组件挂载时已调用过一次dispatch,因此dispatch总共被调用两次。我知道可以使用.toHaveBeenNthCalledWith分别验证两次调用的参数,但需要编写两条expect语句:
expect(store.dispatch).toHaveBeenNthCalledWith(1, 'categoryStore/fetchCategories'); expect(store.dispatch).toHaveBeenNthCalledWith(2, 'categoryStore/fetchCategories');
请问是否有办法通过单条语句实现上述验证?是否有对应的matcher函数或expect对象属性可用?
解决方案
当然有办法用单条语句完成验证!这里给你几个实用的方案:
1. 直接断言mock函数的calls数组
这是最直接的单语句方案,Jest的mock函数会把所有调用的参数存储在mock.calls数组里,每个元素对应一次调用的参数列表。你可以直接验证这个数组的内容,同时确认调用顺序和参数:
expect(store.dispatch.mock.calls).toEqual([ ['categoryStore/fetchCategories'], ['categoryStore/fetchCategories'] ]);
这条语句一次性验证了两次调用的参数都是目标值,且顺序完全符合预期(挂载时的第一次调用,点击按钮的第二次调用)。
2. 结合调用次数与单次匹配(近似单语句体验)
如果觉得直接操作mock.calls不够直观,也可以用两条简短的语句组合,先确认调用次数,再验证所有调用的参数都是目标值:
expect(store.dispatch).toHaveBeenCalledTimes(2); expect(store.dispatch).toHaveBeenCalledWith('categoryStore/fetchCategories');
Jest的.toHaveBeenCalledWith默认会检查至少有一次调用匹配,但结合.toHaveBeenCalledTimes(2)就能确保两次调用的参数都符合要求,这种写法可读性很高。
3. 使用自定义matcher(进阶方案)
如果你的项目里经常需要验证多次相同参数的调用,可以自己封装一个自定义matcher,比如:
expect.extend({ toHaveBeenCalledNTimesWith(received, times, ...args) { const calls = received.mock.calls.filter(call => expect(call).toEqual(args) ); return { pass: calls.length === times, message: () => `Expected function to be called ${times} times with ${args}, but got ${calls.length} times` }; } });
之后就能用单条语句验证:
expect(store.dispatch).toHaveBeenCalledNTimesWith(2, 'categoryStore/fetchCategories');
不过这个方案需要额外封装,适合有重复需求的场景。
总结来说,最推荐第一种方案——直接断言mock.calls数组,它简洁、精准,不需要额外配置就能满足你的需求。
内容的提问来源于stack exchange,提问作者Yeasir Arafat Majumder

