【发布时间】:2021-09-14 14:36:16
【问题描述】:
这是我在 StackOverflow 上的第一个问题。我有一个与工作相关的问题,我已将其重写为关于马里奥的问题。我和我的同事无法想出一个优雅的解决方案,我想知道您是否有任何想法可以帮助我们。
问题:
下面的 switch case 是我们代码中的许多 switch case 之一。下面的一个是我们使用的更简单的案例之一。在这种情况下,有 3 个等级 * 5 个敌人 = 15 种组合。所有组合都有特定的预期难度。
我们更愿意测试所有可能性,因为这是最安全的可靠性方法。我们认为这是必要的,以防止软件开发人员突然说“我要为 DesertLand 中的 Enemy C 引入一个新的难度,即 Medium。”
在我们的代码中有超过 100 种可能性的情况。在某些时候,这会变得非常不舒服(即使有 15 种可能性,我也会感到不舒服)。
可能的解决方案:
#1 每个级别一种方法。优点:每次测试 5 个测试用例。缺点:重复代码。
#2 foreach 方法。优点:更紧凑的代码。缺点:没有对组合和预期结果的概述。
我的问题:
有没有人有一个优雅地解决这个问题的好主意?还是我们不想测试所有的可能性?如果是这样的话?您能解释一下为什么以及为什么您不在工作中这样做吗?
我期待阅读建议。
主要代码:
以下方法根据level和inputModel设置难度:
[assembly: InternalsVisibleTo("MarioTestBase")]
internal class Mario
{
internal void SetDifficulty(InputModel inputModel)
{
switch (this.Level)
{
default:
throw new ArgumentException("This level does not exist.");
case Level.GrassLand:
{
this.Difficulty = Difficulty.Basic;
break;
}
case Level.DesertLand:
{
switch (inputModel.Enemy)
{
default:
throw new ArgumentException("Enemy does not exist.");
case Enemy.A:
case Enemy.B:
case Enemy.C:
case Enemy.D:
case Enemy.E:
{
this.Difficulty = Difficulty.Basic;
break;
}
}
break;
}
case Level.WaterLand:
{
switch (inputModel.Enemy)
{
default:
throw new ArgumentException("Enemy does not exist.");
case Enemy.A:
case Enemy.B:
case Enemy.C:
{
this.Difficulty = Difficulty.Advanced;
break;
}
case Enemy.D:
case Enemy.E:
{
this.Difficulty = Difficulty.Basic;
break;
}
}
break;
}
}
}
}
测试:
我写了以下测试。测试用例基于两个枚举(Level 和 Enemy)并有一个预期的 Enum,即 Difficulty。
[TestCase(Level.GrassLand, Enemy.A, Difficulty.Basic)]
[TestCase(Level.GrassLand, Enemy.B, Difficulty.Basic)]
[TestCase(Level.GrassLand, Enemy.C, Difficulty.Basic)]
[TestCase(Level.GrassLand, Enemy.D, Difficulty.Basic)]
[TestCase(Level.GrassLand, Enemy.E, Difficulty.Basic)]
[TestCase(Level.DesertLand, Enemy.A, Difficulty.Basic)]
[TestCase(Level.DesertLand, Enemy.B, Difficulty.Basic)]
[TestCase(Level.DesertLand, Enemy.C, Difficulty.Basic)]
[TestCase(Level.DesertLand, Enemy.D, Difficulty.Basic)]
[TestCase(Level.DesertLand, Enemy.E, Difficulty.Basic)]
[TestCase(Level.WaterLand, Enemy.A, Difficulty.Advanced)]
[TestCase(Level.WaterLand, Enemy.B, Difficulty.Advanced)]
[TestCase(Level.WaterLand, Enemy.C, Difficulty.Advanced)]
[TestCase(Level.WaterLand, Enemy.D, Difficulty.Basic)]
[TestCase(Level.WaterLand, Enemy.E, Difficulty.Basic)]
public class SetDifficulty_Tests : MarioTestBase
{
public void SetDifficulty_ShouldSelectCorrectDifficulty(Level level, Enemy enemy, Difficulty expectedDifficulty)
{
// Arrange
Mock<Mario> _mock = new Mock<Mario>();
_mock.Setup(x => x.Level).Returns(level);
testModel.Enemy = enemy;
// Act
_mock.Object.SetDifficulty(testModel);
Difficulty actualDifficulty = _mock.Object.Difficulty;
// Assert
Assert.AreEqual(expectedDifficulty, actualDifficulty);
}
}
在集中式类中使用以下SetUp:
public class MarioTestBase
{
protected IMario Mario;
public InputModel testModel;
[SetUp]
public void Setup()
{
Mario = new Mario();
testModel = new InputModel();
}
}
foreach 解决方案的示例实现,我不喜欢:
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Advanced)]
[TestCase(Difficulty.Advanced)]
[TestCase(Difficulty.Advanced)]
[TestCase(Difficulty.Basic)]
[TestCase(Difficulty.Basic)]
public class SetDifficulty_Tests : MarioTestBase
{
public void SetDifficulty_ShouldSelectCorrectDifficulty(Difficulty expectedDifficulty)
{
// Arrange
Mock<Mario> _mock = new Mock<Mario>();
_mock.Setup(x => x.Level).Returns(level);
testModel.Enemy = enemy;
foreach (Level level in Enum.GetValues(typeof(Level)))
{
_mock.Setup(x => x.Level).Returns(level);
foreach (Enemy enemy in Enum.GetValues(typeof(Enemy)))
{
testModel.Enemy = enemy;
_mock.Object.SetDifficulty(inputModel);
Difficulty actualDifficulty = _mock.Object.Difficulty;
Assert.AreEqual(expectedDifficulty, actualDifficulty);
}
}
}
}
【问题讨论】:
标签: c# unit-testing mocking switch-statement system-testing