You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何触发组件错误以测试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);
});

注意事项

  • 测试中需要提前spy snackbar的send方法,不要在触发错误后再调用useSnackbar(),否则会拿到新实例导致断言失败。
  • 第一种方法更贴近真实业务场景,因为前端错误通常来自路由渲染的页面组件。

内容的提问来源于stack exchange,提问作者Aniruth N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:47:06