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

如何正确为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 06:50:04