在Vue组件单元测试中如何验证自定义事件触发次数?
我在测试一个带计数功能的按钮组件时,发现用Vue Test Utils的$emit无法正确验证事件触发次数。组件点击后会连续触发两次自定义事件,但测试总是显示调用次数为0:
// CounterButton.vue
export default {
methods: {
handleClick() {
this.$emit('increment');
this.$nextTick(() => this.$emit('increment'));
}
}
}
测试代码这样写的:
import { mount } from '@vue/test-utils';
describe('CounterButton', () => {
it('should emit increment twice', () => {
const wrapper = mount(CounterButton);
const spy = jest.spyOn(wrapper.vm, '$emit');
wrapper.find('button').trigger('click');
expect(spy.mock.calls.length).toBe(2); // 期望2次实际0次
});
});
尝试过用wrapper.emitted(‘increment’)也返回空对象,是不是$nextTick里的emit需要特别处理?或者spy的使用方式有问题?
jest.spyOn(wrapper.vm, '$emit')监听的是 Vue 实例上的$emit方法本身,但 Vue Test Utils 的mount会用一个包装过的 vm,实际触发事件时走的是内部机制,spy 可能根本没捕获到——尤其在$nextTick异步回调里更难抓。验证事件触发次数的正确姿势是用
wrapper.emitted(),但要记得等$nextTick执行完。你当前测试里没等异步结束就断言,自然拿不到结果。建议改成这样:
如果还担心事件顺序或异步时序问题,可以加个
await nextTick()确保微任务清空。另外emitted()返回的是数组,每个事件名对应一个数组,数组长度就是触发次数,比 spy 更可靠,也避免了对 Vue 内部方法的过度依赖。顺便说一句,如果你组件里经常用
$nextTick触发事件,最好在测试里统一用await nextTick()或者await wrapper.vm.$nextTick()显式等待,不然很容易踩这种“看起来执行了但测试拿不到”的坑。$emit的 spy 没法正确捕获$nextTick里的调用。直接这样改:import { mount } from '@vue/test-utils';
describe('CounterButton', () => {
it('should emit increment twice', async () => {
const wrapper = mount(CounterButton);
await wrapper.find('button').trigger('click');
await wrapper.vm.$nextTick(); // 等待 $nextTick 完成
expect(wrapper.emitted('increment').length).toBe(2); // 直接验证事件触发次数
});
});
别用
spy去监听$emit,用wrapper.emitted()更靠谱,记得加上async/await等$nextTick执行完。