如何正确为Svelte 5的$effect符文编写Vitest单元测试?
解决Svelte 5 $effect在Vitest测试中未销毁的问题
问题原因
你遇到的T1重复输出问题,核心是未主动销毁$effect.root创建的effect上下文:
$effect.root会返回一个销毁函数,若不调用该函数,root内的所有effect会持续监听状态变化;- 测试用例间共享了全局的
$state变量cntr,test2中修改cntr时,test1未销毁的effect仍会触发。
另外,你用$effect.root解决effect_orphan错误是正确的——在组件初始化以外的场景(比如测试环境),$effect必须嵌套在$effect.root中,这是Svelte 5的强制要求,目前没有更优替代方案。
解决方案
1. 手动调用销毁函数清理effect
在每个测试用例结束时,调用$effect.root返回的销毁函数,彻底清理当前测试的effect:
let cntr = $state(1); describe('effect test', () => { test('test1', () => { // 保存root返回的销毁函数 const destroy = $effect.root(() => { $effect(() => { console.log('T1', cntr); }); }); cntr += 1; // 测试结束前销毁所有effect destroy(); }); test('test2', () => { const destroy = $effect.root(() => { $effect(() => { console.log('T2', cntr); }); }); cntr += 1; destroy(); }); });
2. 重置测试间的共享状态
避免测试用例互相干扰,在每个测试前重置$state变量:
let cntr; describe('effect test', () => { // 每个测试用例执行前重置状态 beforeEach(() => { cntr = $state(1); }); test('test1', () => { const destroy = $effect.root(() => { $effect(() => { console.log('T1', cntr); }); }); cntr += 1; destroy(); }); test('test2', () => { const destroy = $effect.root(() => { $effect(() => { console.log('T2', cntr); }); }); cntr += 1; destroy(); }); });
最终效果
修改后测试输出将变为:
T1 2 T2 2
彻底解决旧effect残留触发的问题。
内容的提问来源于stack exchange,提问作者mrh1997
相关产品推荐
相关产品推荐

