先记住这个答案
先按依赖选择测试层次:纯函数和不依赖组件上下文的 ref、computed 逻辑可以直接调用;创建 watcher 等效果时,用明确作用域管理并在测试后停止;使用 onMounted、inject 等组件能力时,用最小组件宿主提供真实上下文。异步请求通过注入可控加载器测试顺序与失败,避免依赖网络速度。涉及真实 DOM、焦点或布局时还需要相应浏览器检查,不能用纯运行时测试代替全部交互验收。
- 按实际依赖选择直接调用、效果作用域或最小组件宿主
- 异步测试主动控制完成顺序,检查旧结果与清理边界
- 测试公开行为,区分模拟运行时与真实 DOM 的覆盖范围
纯响应式逻辑可以从公开接口验证
例如选择状态函数暴露 selectedId、select 和 clear,可以直接调用后检查选择和清空结果,再创建第二个实例确认不会互相污染。不要只断言内部用了 ref 或某个函数调用次数,公开契约才是重构后应继续保持的行为。
对 computed,可以改变输入来源后读取结果,检查普通值、ref 和 getter 是否按约定工作。若创建了 watch 或 watchEffect,则需要在可管理作用域中运行并在结束后清理,避免一个测试留下的效果影响后续测试。作用域停止后的行为也应根据公开契约验证。
生命周期和注入需要真实归属
含 onMounted 的组合式函数直接在普通测试函数里调用,可能没有当前组件实例,因而只得到警告而没有真实挂载行为。可以创建一个最小组件,在 setup 中调用它,挂载后检查状态,再卸载确认订阅解除。这个宿主不必包含完整业务页面。
inject 依赖则需要相应提供者,测试缺失依赖、正常依赖和最近提供者覆盖。简单把 inject 全部替换成固定返回值会跳过连接错误,适合纯逻辑隔离时使用,但还应保留必要的上下文测试,证明组件树实际传递了正确值。
异步测试要控制时间而不是猜时间
请求状态测试可通过加载器返回手动控制的 Promise,按需要安排 A、B 完成顺序,验证过期数据与旧 finally 不影响当前状态。还要覆盖拒绝、空输入和销毁后的晚到结果。等待一个固定毫秒数容易受机器速度影响,也难以说明哪一步真正发生。
nextTick 用于等待 Vue 的更新调度,不会替尚未解决的网络 Promise 生成结果;刷新 Promise 队列的工具也不能代替控制外部操作。涉及事件冒泡、输入焦点或布局的逻辑,应在合适 DOM 环境或真实浏览器中补验,并在测试报告中明确纯运行时、模拟 DOM 和真实浏览器各自覆盖了什么。
容易答错的地方
- 所有 composable 都能脱离组件直接测
- 使用生命周期或注入等能力时需要对应上下文,不能只因实现是普通函数就忽略依赖。排查“Vue 组合式函数单元测试 effectScope 与组件宿主”时还要核对输入、版本和执行顺序,并用反例确认修复后的边界。
- await nextTick 会完成所有异步请求
- 它处理 Vue 更新时机,外部 Promise、计时器和网络完成条件需要分别控制。排查“Vue 组合式函数单元测试 effectScope 与组件宿主”时还要核对输入、版本和执行顺序,并用反例确认修复后的边界。
面试官还会怎么问?
effectScope 能触发 onMounted 吗?
不能仅靠它触发组件挂载。它管理响应式效果的范围,生命周期仍需要组件实例及相应挂载过程。针对“Vue 组合式函数单元测试 effectScope 与组件宿主”,还应保留最小复现、预期结果和失败路径,避免只凭一次现象下结论。
可以只 mock 网络请求吗?
可以用替代加载器稳定控制网络结果,但组件逻辑仍应使用实际响应式运行时,另外保留必要的真实接口集成验证。
如何避免测试跟实现绑定太紧?
围绕输入变化、返回状态、动作结果和资源释放判断,少依赖内部变量名或没有业务意义的中间调用顺序。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。