c# - 如何测试使用定义的 getter 更改变量的公共方法
问题描述
我有一个有硬币的游戏。有一个 Add() 方法可以更改硬币。我想测试它是否正确添加。
public class CoinsService
{
public ReactiveProperty<long> Coins { get { return State.SaveGame.Coins; } }
public int Add(int coins)
{
Coins.Value += coins;
return coins;
}
}
考试:
public class CoinServiceTests
{
[Test]
public void AddCoins_WhenCalled_AddsUpToTotalCoins()
{
var coinsService = new CoinsService();
coinsService.Add(10);
Assert.That(coinsService.Coins.Value, Is.EqualTo(10));
}
}
我尝试过使用 NSubstitute 来替代该类:
var coinsService = Substitute.For<CoinsService>();
并像这样实例化一个新的硬币实例
coinsService.Coins.Returns(new ReactiveProperty<long>());
也这样
var coins = new ReactiveProperty<long>();
coinsService.Coins.Returns(coins);
我希望在执行上述任何操作后,我将能够检查 Coins 的价值。相反,我得到一个空引用对象异常,该coinsService.Coins
对象为空
为了澄清,空引用出现在该行
public ReactiveProperty<long> Coins { get { return State.SaveGame.Coins; } }
解决方案
您正在尝试测试CoinsService
,因为它会添加。因此,您必须使用真实的 CoinsService
而不是模拟它。模拟适用于与您尝试测试的课程协作的课程。
看看你的代码,我明白你为什么认为这应该工作......你有这行代码......
coinsService.Coins.Returns(new ReactiveProperty<long>());
这会导致每次访问时都会创建一个新Coins
属性,这就是您看到错误的原因。
我怀疑这CoinsService
是一个大类,有很多功能,你不想实例化只是为了测试添加硬币的能力。这导致想要嘲笑它。(如果这不是真的,我们可以停在这里——只是不要嘲笑它!)
如果CoinsService
“太大而无法测试”,则需要以使用协作者的方式对其进行分解。例如,想象那Coins
是一堂课,而不仅仅是一堂课。它可能有一个 Add 方法......Add(long howmuch)
例如。
然后CoinsService
将被改变只是有点做......
public int Add(int coins)
{
Coins.Add(coins);
return Coins.Value; // I believe your original return is in error
}
现在这使它变得更加间接,但给您带来的优势是您可以通过测试 Coins 类来测试加法功能,而无需使用服务。
您还可以(并且应该)通过为 Coins 创建一个模拟并确保在调用它的Add
方法时CoinService.Add
调用它来测试服务本身。