Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing Url.IsLocalUrl(returnUrl.ToString()), how can I get it to return false in unit test?

From the standard LogOn method in the account controller in MVC3 applications, how can I test the

Url.IsLocalUrl(returnUrl.ToString()) 

line of code where the url is not local? In other words, what url do I have to feed into this line of code when unit testing, to get it to return false?

I have used the following thinking this would be returned as false(non local):

Uri uri = new Uri(@"http://www.google.com/blahblah.html");

But it just threw a null exception in the unit tests

Edit: I should add that the LogOn method now looks like this:

public ActionResult LogOn(LogOnModel model, System.Uri returnUrl)

if (ModelState.IsValid) {

            bool loggedOn = LogOn(model);

            if (loggedOn) {
                if (Url.IsLocalUrl(returnUrl.ToString())) {
                    return Redirect(returnUrl.ToString());
                }
                else {
                    return RedirectToAction("Index", "Home");
                }
            }
            else {
                ModelState.AddModelError("", "The user name or password provided is incorrect.");
            }
        }

        // If we got this far, something failed, redisplay form
        return View(viewModel);
    }

Some style cop/code analysis errors forced a changed from a string parameter to System.uri parameter but it is very similar to the standard original.

Just to clarify, in a unit test - I want to test and assert the outcome of hitting the Else line where it redirects to Home/Index, so I need to pass something to into the (System.Uri)returnUrl that will make it return false on Url.IsLocalUrl and not throw an exception

Further Edit:

I am using MvcContrib testhelper, which is pretty good at mocking a lot of the httpcontext and web stuff:

Builder = new TestControllerBuilder();
UserController = new UserController();
    Builder.InitializeController(UserController);
like image 273
DevDave Avatar asked Feb 27 '12 10:02

DevDave


People also ask

How will you unit test a controller?

Unit tests of controller logic. Unit tests involve testing a part of an app in isolation from its infrastructure and dependencies. When unit testing controller logic, only the contents of a single action are tested, not the behavior of its dependencies or of the framework itself.


2 Answers

You need to mock the HttpContext as well as the UrlHelper instance on the controller that you are unit testing. Here's an example of how that unit test might look like if you are using Moq:

[TestMethod]
public void LogOn_Should_Redirect_To_Home_If_Authentication_Succeeds_But_Not_Local_ReturnUrl_Is_Provided()
{
    // arrange
    var sut = new AccountController();
    var model = new LogOnModel();
    var returnUrl = new Uri("http://www.google.com");
    var httpContext = new Mock<HttpContextBase>();
    var request = new Mock<HttpRequestBase>();
    httpContext.Setup(x => x.Request).Returns(request.Object);
    request.Setup(x => x.Url).Returns(new Uri("http://localhost:123"));
    var requestContext = new RequestContext(httpContext.Object, new RouteData());
    sut.Url = new UrlHelper(requestContext);

    // act
    var actual = sut.LogOn(model, returnUrl);

    // assert
    Assert.IsInstanceOfType(actual, typeof(RedirectToRouteResult));
    var result = (RedirectToRouteResult)actual;
    Assert.AreEqual("Home", result.RouteValues["controller"]);
    Assert.AreEqual("Index", result.RouteValues["action"]);
}

Remark: since you have actually shown the LogOn implementation that you are calling to verify the credentials, you might need to adapt the unit test to ensure that this method returns true in the first place given the model in order to enter the if (loggedOn) clause.


UPDATE:

It seems that you are using MvcContrib.TestHelper which does all the HttpContext mocking setup for you. So all that you need to do is to mock the relevant parts for your unit test:

[TestMethod]
public void LogOn_Should_Redirect_To_Home_If_Authentication_Succeeds_But_Not_Local_ReturnUrl_Is_Provided()
{
    // arrange
    var sut = new AccountController();
    new TestControllerBuilder().InitializeController(sut);
    var model = new LogOnModel();
    var returnUrl = new Uri("http://www.google.com");
    sut.HttpContext.Request.Expect(x => x.Url).Return(new Uri("http://localhost:123"));

    // act
    var actual = sut.LogOn(model, returnUrl);

    // assert
    actual
        .AssertActionRedirect()
        .ToController("Home")
        .ToAction("Index");
}

Normally the first 2 lines of the unit test could be moved to the global [SetUp] method to avoid repeating them in each unit test for this controller so that now your test becomes a bit cleaner:

[TestMethod]
public void LogOn_Should_Redirect_To_Home_If_Authentication_Succeeds_But_Not_Local_ReturnUrl_Is_Provided()
{
    // arrange
    var model = new LogOnModel();
    var returnUrl = new Uri("http://www.google.com");
    _sut.HttpContext.Request.Expect(x => x.Url).Return(new Uri("http://localhost:123"));

    // act
    var actual = _sut.LogOn(model, returnUrl);

    // assert
    actual
        .AssertActionRedirect()
        .ToController("Home")
        .ToAction("Index");
}
like image 59
Darin Dimitrov Avatar answered Sep 25 '22 00:09

Darin Dimitrov


I landed here searching for how the null reference in my controller and the current answer is a bit dated. I managed just fine doing this using moq:

var mockUrlHelper = new Mock<IUrlHelper>();
mockUrlHelper.Setup(m => m.IsLocalUrl(It.IsAny<string>())).Returns(false);

var controller = new MyController();
controller.Url = mockUrlHelper.Object;
like image 20
span Avatar answered Sep 22 '22 00:09

span