使用 NSubstitute 将 Arg.Any<int>() 作为参数传递时出现 NullReferenceException

NullReferenceException when passing Arg.Any<int>() as argument using NSubstitute

我有一个客户 class,它接受 IDbGateway 接口作为构造函数参数。我需要使用 NUnit 和 NSubstitute 为 class 编写单元测试 CalculateWage_HourlyPayed_ReturnsCorrectWage。当我通过 anyId 时,我的单元测试工作正常。但我想传递 Arg.Any<int>() 而不是 anyId。目前我的测试失败了,因为 decimal actual = sut.CalculateWage(Arg.Any<int>()); 给出了 NullReferenceException。为什么我的代码适用于 anyId 值传递但不适用于 Arg.Any<int>()?

目标成员如下

public class Customer
{
    private readonly IDbGateway _gateway;

    public Customer(IDbGateway gateway)
    {
        _gateway = gateway;
    }

    public decimal CalculateWage(int id)
    {
        WorkingStatistics ws = _gateway.GetWorkingStatistics(id);

        decimal wage;

        if (ws.PayHourly)
        {
            wage = ws.WorkingHours * ws.HourSalary;
        }
        else
        {
            wage = ws.MonthSalary;
        }

        return wage;
    }
}

public interface IDbGateway
{
    WorkingStatistics GetWorkingStatistics(int id);
}

public class DbGateway : IDbGateway
{
    public WorkingStatistics GetWorkingStatistics(int id)
    {
        throw new NotImplementedException();
    }
}
public class WorkingStatistics
{
    public decimal HourSalary { get; set; }
    public int WorkingHours { get; set; }
    public decimal MonthSalary { get; set; }
    public bool PayHourly { get; set; }
}

这是有问题的测试

[TestFixture]
public class CustomerTestsWithNSubstitute
{
    [Test]
    public void CalculateWage_HourlyPayed_ReturnsCorrectWage()
    {
        var gateway = Substitute.For<IDbGateway>();
        var workingStatistics = new Business.Demo.WorkingStatistics()
        { PayHourly = true, HourSalary = 100, WorkingHours = 10 };
        const int anyId = 1;
        //gateway.GetWorkingStatistics(anyId).ReturnsForAnyArgs(workingStatistics);
        gateway.GetWorkingStatistics(Arg.Any<int>()).ReturnsForAnyArgs(workingStatistics);

        const decimal expectedWage = 100 * 10;
        var sut = new Customer(gateway);

        //decimal actual = sut.CalculateWage(anyId);
        decimal actual = sut.CalculateWage(Arg.Any<int>());

        Assert.That(actual, Is.EqualTo(expectedWage).Within(0.1));
    }
}

Arg.* 用于 arranging/stubbing 模拟行为。

它们并不意味着在进行测试时用作实际参数值

Argument matchers should only be used when specifying calls for the purposes of setting return values, checking received calls, or configuring callbacks (for example: with Returns, Received or When). Using Arg.Is or Arg.Any in other situations can cause your tests to behave in unexpected ways.

引用How NOT to use argument matchers

[TestFixture]
public class CustomerTestsWithNSubstitute {
    [Test]
    public void CalculateWage_HourlyPayed_ReturnsCorrectWage() {
        //Arrange
        var gateway = Substitute.For<IDbGateway>();
        var workingStatistics = new Business.Demo.WorkingStatistics()
        { PayHourly = true, HourSalary = 100, WorkingHours = 10 };
        const int anyId = 1;
        
        //Use Arg.* here
        gateway.GetWorkingStatistics(Arg.Any<int>()).ReturnsForAnyArgs(workingStatistics);

        const decimal expectedWage = 100 * 10;
        var sut = new Customer(gateway);

        //Act
        decimal actual = sut.CalculateWage(anyId); //<-- Use actual value here

        //Assert    
        Assert.That(actual, Is.EqualTo(expectedWage).Within(0.1));
    }
}

引用Argument matchers