3

I have the following method in my HomeController. The purpose is to split users based on IP address to test different versions of the home page:

        [HttpGet]
        public ActionResult Index()
        {
            var userIpAddress = GetUserIpAddress();

            if (IsIpAddressOddOrEven(userIpAddress))
            {
                return RedirectToAction(HomePage);
            }

            return RedirectToAction(HomePageAlternative);
        }

The GetUserIpAddress method:

private string GetUserIpAddress()
        {
            HttpContext context = System.Web.HttpContext.Current;
            var ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

            if (!string.IsNullOrEmpty(ipAddress))
            {
                string[] addresses = ipAddress.Split(',');
                if (addresses.Length != 0)
                {
                    return addresses[0];
                }
            }
            return context.Request.ServerVariables["REMOTE_ADDR"];
        }

I want to write a unit test to ensure that this works properly. However, every time the unit test runs it is just taking the IP address I currently have. I am struggling to work out how to mock the result of the 'GetUserIpAddress' method to return an odd or even string. My attempt so far:

        [Test]
        public void Test()
        {
            var controller = CreateMvcController<HomeController>();

            var result = controller.Index();
            controller.HttpContext.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].Returns("1");
            Assert.IsInstanceOf<RedirectToRouteResult>(result);

            var redirectToRouteResult = result as RedirectToRouteResult;

            Assert.AreEqual(HomeController.HomePage, redirectToRouteResult.RouteValues["action"]);
        }

I got the error that the result of controller.HttpContext.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].Returns("1"); is not a string but a HttpResponseBase, and in any case I am not convinced I am going about this the right way. Please can someone point me in the right direction to do this? Thank you

Jordan1993
  • 864
  • 1
  • 10
  • 28

1 Answers1

2

I suggest you to move GetUserIpAddress method to a helper class which you can inject into HomeController. Therefore you can mock it while doing the unit tests.

Your HomeController will be like this

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

So, you'll be able to mock UserIpAddressHelper and inject it when writing the test.

public void Test()
{
    var userIpAddressHelper = Substitute.For<IUserIpAddressHelper>();
    userIpAddressHelper.GetUserIpAddress(Arg.Any<HttpContext>()).Returns("0.0.0.0");
  
    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"]);
}
Koray Elbek
  • 794
  • 5
  • 13
  • Thank you so much for this @CoReeYe - I think this is a clean solution. However, my test is now failing - when I debug this is because `userIpAddress` is "" rather than "0.0.0.0" - do you know why this might be ? – Jordan1993 Aug 20 '20 at 13:13
  • Hello, I've suggested a new solution at your other question: https://stackoverflow.com/questions/63506073/nsubstitute-returning-empty-string/63506712#63506712 – Koray Elbek Aug 20 '20 at 13:58