Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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

I have a customer class which accepts an IDbGateway interface as a constructor parameter. I need to write a unit test CalculateWage_HourlyPayed_ReturnsCorrectWage for the class using NUnit and NSubstitute. My unit test works fine when I pass anyId. But I want to pass Arg.Any<int>() instead of anyId. At the moment my test fails because decimal actual = sut.CalculateWage(Arg.Any<int>()); gives NullReferenceException. Why is my code working for anyId value pass but not for Arg.Any<int>()?

Here are the target members

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; }
}

Here is the test in question

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

[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));
    }
}

>Solution :

Arg.* are meant to be used in arranging/stubbing the mocked behavior.

They are not meant to be used as actual parameter values when exercising the test

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.

Reference 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));
    }
}

Reference Argument matchers

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading