如何触发组件错误以测试Vue3的onErrorCaptured生命周期钩子?
如何触发Vue组件错误以测试onErrorCaptured钩子?
问题背景
我在App.vue中使用Vue3的onErrorCaptured生命周期钩子捕获错误,实现打印错误日志并将错误发送至snackbar的功能:
<script setup> import { onErrorCaptured } from "vue"; import { TheLayout } from "@/components"; const snackbar = useSnackbar(); onErrorCaptured((err) => { console.error(err); snackbar.send(err); return false; }); </script> <template> <the-layout> <router-view /> </the-layout> </template>
我希望测试该钩子的行为,目前已编写了部分测试代码,但不清楚如何触发组件错误来完成测试,测试代码如下:
it("should catch errors and logs and send them to snackbar", async () => { const consoleErrorSpy = vi.spyOn(console, "error"); const wrapper = mount(App, { global: { plugins: [ styles, router, i18n, createTestingPinia({ initialState: { snackbarStore: { show: false, message: "" } } }) ] } }); const error = new Error("error message"); // 这里需要触发错误的代码 await nextTick(); const snackbar = useSnackbar(); expect(consoleErrorSpy).toHaveBeenCalledWith(error); expect(snackbar.send).toHaveBeenCalledWith(error); });
解决方案
有几种可靠的方式可以触发组件错误,让onErrorCaptured钩子捕获:
方法1:通过路由渲染一个抛出错误的临时组件
利用App中的<router-view>,动态添加一个会抛出错误的路由组件,触发错误捕获:
it("should catch errors and logs and send them to snackbar", async () => { const consoleErrorSpy = vi.spyOn(console, "error"); // 提前监听snackbar的send方法 const snackbarStore = useSnackbar(); const sendSpy = vi.spyOn(snackbarStore, "send"); const wrapper = mount(App, { global: { plugins: [ styles, router, i18n, createTestingPinia({ initialState: { snackbarStore: { show: false, message: "" } } }) ] } }); const error = new Error("error message"); // 添加临时路由,组件在setup阶段抛出错误 router.addRoute({ path: "/test-error", component: { setup() { throw error; }, render() { return null; } } }); // 跳转到该路由,触发组件渲染错误 await router.push("/test-error"); await nextTick(); expect(consoleErrorSpy).toHaveBeenCalledWith(error); expect(sendSpy).toHaveBeenCalledWith(error); });
方法2:挂载一个抛出错误的临时子组件
如果不想修改路由,可以直接在App的插槽中挂载一个会抛出错误的组件:
it("should catch errors and logs and send them to snackbar", async () => { const consoleErrorSpy = vi.spyOn(console, "error"); const snackbarStore = useSnackbar(); const sendSpy = vi.spyOn(snackbarStore, "send"); const wrapper = mount(App, { global: { plugins: [ styles, router, i18n, createTestingPinia({ initialState: { snackbarStore: { show: false, message: "" } } }) ] } }); const error = new Error("error message"); // 创建一个在setup阶段抛出错误的组件 const FaultyComponent = { setup() { throw error; }, render() { return null; } }; // 将故障组件挂载到TheLayout的默认插槽 const layout = wrapper.findComponent(TheLayout); layout.vm.$slots.default = [wrapper.vm.$createElement(FaultyComponent)]; // 强制更新组件触发错误 layout.vm.$forceUpdate(); await nextTick(); expect(consoleErrorSpy).toHaveBeenCalledWith(error); expect(sendSpy).toHaveBeenCalledWith(error); });
注意事项
- 测试中需要提前
spysnackbar的send方法,不要在触发错误后再调用useSnackbar(),否则会拿到新实例导致断言失败。 - 第一种方法更贴近真实业务场景,因为前端错误通常来自路由渲染的页面组件。
内容的提问来源于stack exchange,提问作者Aniruth N
相关产品推荐
相关产品推荐

