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