Files
zeling_v2/Docs/Verification/03_Unit_StatusEffects.md

7.2 KiB
Raw Permalink Blame History

单元测试 03 · 状态效果系统StatusEffects

测试类型EditMode 单元测试NUnit
测试文件Assets/Tests/EditMode/StatusEffectTests.cs(已存在,本文为完整规范)
被测程序集BaseGames.Combat.StatusEffects
asmdef 依赖BaseGames.Tests.EditMode.asmdef 需引用 BaseGames.Combat.StatusEffects


目录

  1. 测试覆盖范围
  2. 现有测试验证
  3. 补充测试说明
  4. 完整扩展测试代码
  5. 手动集成验证Play Mode

1. 测试覆盖范围

测试点
FireEffect MaxStacks=1MutualExclusions 含 FreezeOnStack 刷新持续时间EffectType=Fire
PoisonEffect MaxStacks=3MutualExclusions 为空OnStack 增加 StackCount超限截断EffectType=Poison
StaggerEffect MaxStacks=1BlockedBy 含 StunEffectType=Stagger
StatusEffect 基类 IsExpired 在 Duration 耗尽后为 trueUpdate 正确减少剩余时间
StatusEffectManager ApplyEffect 正确添加;互斥效果自动移除;到期效果自动清理(需 MonoBehaviour Test 或接口 Mock

2. 现有测试验证

项目已存在 Assets/Tests/EditMode/StatusEffectTests.cs,覆盖以下测试(共 14 个):

测试名 验证点
FireEffect_MaxStacks_IsOne FireEffect.MaxStacks == 1
PoisonEffect_MaxStacks_IsThree PoisonEffect.MaxStacks == 3
PoisonEffect_OnStack_IncreasesStackCount OnStack 后 StackCount == 2
PoisonEffect_OnStack_ClampsAtMaxStacks 多次叠加不超过 MaxStacks
StaggerEffect_MaxStacks_IsOne StaggerEffect.MaxStacks == 1
FireEffect_MutualExclusions_ContainsFreeze MutualExclusions 含 Freeze
PoisonEffect_MutualExclusions_IsEmpty PoisonEffect 无互斥
StaggerEffect_BlockedBy_ContainsStun BlockedBy 含 Stun
FireEffect_BlockedBy_IsEmpty FireEffect 无阻断
StatusEffect_IsExpired_AfterDurationDepleted 时间耗尽后 IsExpired == true
FireEffect_OnStack_RefreshDuration OnStack 刷新剩余时间
FireEffect_EffectType_IsFire EffectType == StatusEffectType.Fire
PoisonEffect_EffectType_IsPoison EffectType == StatusEffectType.Poison
StaggerEffect_EffectType_IsStagger EffectType == StatusEffectType.Stagger

运行现有测试步骤:

  1. 打开 Window → General → Test Runner
  2. 选择 EditMode 标签页
  3. 展开 BaseGames.Tests.EditMode → StatusEffectTests
  4. 点击 Run All(或右键 Run
  5. 确认所有 14 个测试 全部绿色

3. 补充测试说明

现有测试未覆盖以下场景,需补充:

3.1 StatusEffect.Update 时间精度

验证 Update(delta) 正确累计时间,不因浮点精度导致 IsExpired 判断提前/延迟。

3.2 多效果独立计时

同时持有 FireEffectPoisonEffect 时,两个效果的剩余时间独立递减,互不影响。

3.3 OnApply / OnRemove 回调

OnApply(owner)OnRemove(owner) 在正确时机被调用owner 可传 null 用于纯计时测试)。

3.4 StackCount 初始值

新建 Effect 时 StackCount == 1(已 Apply 一层)。


4. 完整扩展测试代码

将以下代码追加到现有 StatusEffectTests.cs} 前,或创建新文件 StatusEffectExtendedTests.cs

// 追加到 BaseGames.Tests.EditMode 命名空间下StatusEffectTests 类中

// ── 补充测试 ────────────────────────────────────────────────────────────────

[Test]
public void StatusEffect_StackCount_InitialValueIsOne()
{
    var effect = new FireEffect();
    Assert.AreEqual(1, effect.StackCount, "新建 Effect 的初始 StackCount 应为 1");
}

[Test]
public void StatusEffect_Update_RemainingTime_Decreases()
{
    var effect = new StaggerEffect(2.0f);
    effect.OnApply(null);
    float initialDuration = effect.Duration;

    effect.Update(0.5f);

    Assert.Less(effect.Duration, initialDuration, "Update 后剩余时间应减少");
    Assert.IsFalse(effect.IsExpired, "0.5s Update 后 2.0s 效果不应过期");
}

[Test]
public void StatusEffect_Update_ExactExpiry()
{
    var effect = new StaggerEffect(1.0f);
    effect.OnApply(null);

    effect.Update(1.0f); // 恰好耗尽

    Assert.IsTrue(effect.IsExpired, "恰好耗尽 duration 后应 IsExpired == true");
}

[Test]
public void MultipleEffects_IndependentTimers()
{
    var fire   = new FireEffect();
    var poison = new PoisonEffect();
    fire.OnApply(null);
    poison.OnApply(null);

    float fireDuration   = fire.Duration;
    float poisonDuration = poison.Duration;

    fire.Update(0.5f);

    // poison 未调用 Update时间不应变化
    Assert.AreEqual(poisonDuration, poison.Duration, 0.0001f,
        "未 Update 的 Effect 持续时间不应减少");
    Assert.Less(fire.Duration, fireDuration, "已 Update 的 Effect 持续时间应减少");
}

[Test]
public void PoisonEffect_OnStack_DoesNotExceedMaxStacks_EdgeCase()
{
    var effect = new PoisonEffect();
    // MaxStacks = 3初始 StackCount = 1再叠加 2 次达到上限
    effect.OnStack();
    effect.OnStack();
    Assert.AreEqual(3, effect.StackCount);

    // 再叠加,不应超过 3
    effect.OnStack();
    effect.OnStack();
    Assert.AreEqual(3, effect.StackCount, "StackCount 不应超过 MaxStacks");
}

[Test]
public void FireEffect_OnApply_ThenRemove_DoesNotThrow()
{
    var effect = new FireEffect();
    Assert.DoesNotThrow(() =>
    {
        effect.OnApply(null);
        effect.OnRemove(null);
    });
}

5. 手动集成验证Play Mode

以下场景需要在 Unity Editor 中手动测试(无法在 EditMode 测试中验证):

5.1 StatusEffectManager 添加效果

前提条件:测试场景中有玩家或敌人,其 GameObject 挂有 StatusEffectManager 组件。

步骤:

  1. 进入 Play Mode
  2. 在 Inspector 中找到目标对象的 StatusEffectManager 组件
  3. 通过 Console 调用(或临时测试按钮):statusEffectManager.Apply(new PoisonEffect())
  4. 在 Console 中观察 Poison 效果 Tick 日志(每 tickInterval 秒一条)

预期结果:

  • Poison 效果持续 duration
  • 每次 Tick 扣减目标 HP
  • 到期后自动从管理器移除,不再 Tick

5.2 互斥效果测试

步骤:

  1. 对同一目标施加 FireEffect
  2. 随后施加 FreezeEffectFireEffect 的 MutualExclusion

预期结果:

  • FireEffect 被自动移除
  • Console 无 NullReferenceException

5.3 效果叠加Poison

步骤:

  1. 对目标施加 PoisonEffectStackCount = 1
  2. 再施加一次 PoisonEffect
  3. 再施加一次StackCount 达到 3
  4. 再施加一次(超过上限)

预期结果:

  • StackCount 分别变为 2、3、3截断
  • Inspector 中 StatusEffectManager 显示正确的 StackCount

5.4 阻断效果StaggerEffect BlockedBy Stun

步骤:

  1. 对目标先施加 StunEffect
  2. 尝试施加 StaggerEffect

预期结果:

  • StaggerEffect 被阻断,未添加到管理器
  • Console 无报错