首页 > 解决方案 > 如何测试使用定义的 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; } }

标签: c#unit-testingmockingnunitnsubstitute

解决方案


您正在尝试测试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调用它来测试服务本身。


推荐阅读