Search code examples
c#asp.net-mvcnunitnsubstitute

NSubstitute returning empty string


I have the following method under test:

public HomeController(IUserIpAddressHelper userIpAddressHelper)
{
    _userIpAddressHelper = userIpAddressHelper;
}

[HttpGet]
public ActionResult Index()
{
    var userIpAddress = _userIpAddressHelper.GetUserIpAddress(System.Web.HttpContext.Current);
    if (_userIpAddressHelper.IsIpAddressOddOrEven(userIpAddress))
    {
        return RedirectToAction(HomePage);
    }
    
    return RedirectToAction(HomePageAlternative);
}

and I am testing as follows:

public void Test()
{
    var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
    userIpAddressHelper.GetUserIpAddress(Arg.Any<HttpContext>()).Returns("0.0.0.2");
  
    var controller = new HomeController(userIpAddressHelper);

    var result = controller.Index();

    Assert.IsInstanceOf<RedirectToRouteResult>(result);

    var redirectToRouteResult = result as RedirectToRouteResult;
    Assert.AreEqual(HomeController.HomePage, redirectToRouteResult.RouteValues["action"]);
}

However the test is failing due to the value of userIpAddress being "" an empty string, instead of 0.0.0.2 as I've set it. Can anyone please point out where I've gone wrong here?


Solution

  • Is userIpAddress definitely ""? It looks like the Returns in your original test is specified well, but if IUserIpAddressHelper is an interface then the substitute for it will not have a result stubbed for IsIpAddressOddOrEven, so it will always return false even if GetUserIpAddress is stubbed to return "0.0.0.2".

    To get the test to mirror how the production code passes through the data, you can stub out both members:

    var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
    userIpAddressHelper.GetUserIpAddress(Arg.Any<HttpContext>()).Returns("0.0.0.2");
    userIpAddressHelper.IsIpAddressOddOrEven("0.0.0.2").Returns(true);
    

    This will test that the production code correctly passes through the result of GetUserIpAddress to IsIpAddressOddOrEven.

    Note: we could also stub these to work with "ip-address-result" and it would still work. We don't need a valid odd/even result returned, as we are not using a real implementation of IUserIpAddressHelper, just a substitute for testing. If you find it necessary to substitute for IUserIpAddressHelper in lots of tests and you want it to act like a real implementation (i.e. it will actually return whether an address is odd or even), it might be easier to write a TestUserIpAddressHelper.

    Another way to avoid having the dependency between the results of GetUserIpAddress and IsIpAddressOddOrEven is to change the interface to have a bool IsIpAddressOddOrEven(HttpContext context) method that combines both operations. That way you would only need to stub one for the test.