Vue3+Vitest+Test-utils中Mock路由变更如何触发watch监听器
Vue3单元测试中Mock路由无法触发watch监听的解决办法
问题场景
我在Vue3单元测试中通过vi.mock模拟vue-router的useRoute方法:
vi.mock('vue-router', () => { return { useRoute: vi.fn(() => ({query: 'test'})), }; });
测试用例中尝试修改路由的query属性:
it('updates query', ()=>{ wrapper.vm.route.query = 'new value' })
但组件内监听route.query的watch完全没有触发:
const route = useRoute() watch(route.query, () => { console.log('TRIGGERED'); });
将Mock路由替换为普通响应式对象后一切正常,推测Mock的路由对象被Vue的watch忽略,想知道如何绕过该限制。
原因分析
Vue的watch依赖响应式系统工作:
- 当前Mock的
route是普通JS对象,query并非响应式属性,直接修改无法触发依赖追踪 - 组件内通过
useRoute获取的route与wrapper.vm.route可能并非同一引用,直接修改后者无法影响组件内的监听目标
可行解决方案
方案1:让Mock路由成为响应式对象
用Vue的reactive包裹Mock的路由对象,使query变为响应式属性:
import { reactive } from 'vue'; vi.mock('vue-router', () => { // 创建响应式路由对象 const mockRoute = reactive({ query: 'test' }); return { useRoute: vi.fn(() => mockRoute), }; });
测试用例中直接修改该响应式对象的query,并触发Vue更新:
// 获取Mock的路由实例 const mockRoute = vi.mocked(useRoute).mock.results[0].value; it('updates query', async ()=>{ mockRoute.query = 'new value'; // 等待Vue完成更新 await wrapper.vm.$nextTick(); // 执行断言逻辑,比如检查组件是否做出对应响应 });
方案2:模拟真实路由的引用替换行为
真实场景中路由变化时,useRoute会返回一个新的路由对象,可通过ref模拟该行为:
import { ref } from 'vue'; vi.mock('vue-router', () => { const routeRef = ref({ query: 'test' }); return { useRoute: vi.fn(() => routeRef.value), // 暴露内部更新方法,供测试用例调用 _updateRoute: (newRoute) => { routeRef.value = newRoute; } }; });
测试用例中通过暴露的方法更新路由:
import { _updateRoute } from 'vue-router'; it('updates query', async ()=>{ _updateRoute({ query: 'new value' }); await wrapper.vm.$nextTick(); // 执行断言逻辑 });
方案3:调整组件内的监听逻辑
如果不想修改Mock代码,可调整组件内的watch监听整个route对象并开启深度监听:
const route = useRoute() watch(route, () => { console.log('TRIGGERED'); }, { deep: true });
注意:该方案仅适配当前Mock场景,真实路由环境中监听整个route无需开启deep(因为路由变化时是返回新对象)。
内容的提问来源于stack exchange,提问作者Tumulte
相关产品推荐
相关产品推荐

