7.2 KiB
7.2 KiB
单元测试 03 · 状态效果系统(StatusEffects)
测试类型:EditMode 单元测试(NUnit)
测试文件:Assets/Tests/EditMode/StatusEffectTests.cs(已存在,本文为完整规范)
被测程序集:BaseGames.Combat.StatusEffects
asmdef 依赖:BaseGames.Tests.EditMode.asmdef需引用BaseGames.Combat.StatusEffects
目录
1. 测试覆盖范围
| 类 | 测试点 |
|---|---|
FireEffect |
MaxStacks=1;MutualExclusions 含 Freeze;OnStack 刷新持续时间;EffectType=Fire |
PoisonEffect |
MaxStacks=3;MutualExclusions 为空;OnStack 增加 StackCount;超限截断;EffectType=Poison |
StaggerEffect |
MaxStacks=1;BlockedBy 含 Stun;EffectType=Stagger |
StatusEffect 基类 |
IsExpired 在 Duration 耗尽后为 true;Update 正确减少剩余时间 |
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 |
运行现有测试步骤:
- 打开
Window → General → Test Runner - 选择
EditMode标签页 - 展开
BaseGames.Tests.EditMode → StatusEffectTests - 点击
Run All(或右键Run) - 确认所有 14 个测试 全部绿色
3. 补充测试说明
现有测试未覆盖以下场景,需补充:
3.1 StatusEffect.Update 时间精度
验证 Update(delta) 正确累计时间,不因浮点精度导致 IsExpired 判断提前/延迟。
3.2 多效果独立计时
同时持有 FireEffect 和 PoisonEffect 时,两个效果的剩余时间独立递减,互不影响。
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 组件。
步骤:
- 进入 Play Mode
- 在 Inspector 中找到目标对象的
StatusEffectManager组件 - 通过 Console 调用(或临时测试按钮):
statusEffectManager.Apply(new PoisonEffect()) - 在 Console 中观察 Poison 效果 Tick 日志(每
tickInterval秒一条)
预期结果:
- Poison 效果持续
duration秒 - 每次 Tick 扣减目标 HP
- 到期后自动从管理器移除,不再 Tick
5.2 互斥效果测试
步骤:
- 对同一目标施加
FireEffect - 随后施加
FreezeEffect(FireEffect 的 MutualExclusion)
预期结果:
FireEffect被自动移除- Console 无
NullReferenceException
5.3 效果叠加(Poison)
步骤:
- 对目标施加
PoisonEffect(StackCount = 1) - 再施加一次
PoisonEffect - 再施加一次(StackCount 达到 3)
- 再施加一次(超过上限)
预期结果:
- StackCount 分别变为 2、3、3(截断)
- Inspector 中
StatusEffectManager显示正确的 StackCount
5.4 阻断效果(StaggerEffect BlockedBy Stun)
步骤:
- 对目标先施加
StunEffect - 尝试施加
StaggerEffect
预期结果:
StaggerEffect被阻断,未添加到管理器- Console 无报错