Testing Account/Logon Action - asp.net-mvc

I'm testing the Account/Loggon action using the built-in testing tool of Visual Studio 2010 and the class library from this article to create a fake controller context.
When I run the test method, this code line:
FormsAuthentication.SetAuthCookie(username, false);
throws an exception: Object reference not set to an instance of an object
To test the loggon action, I think I should create a controller with a fake controller context that has a cookie collection. Here is my testing code block:
AccountController controller = new AccountController();
var cookies = new HttpCookieCollection();
controller.ControllerContext = new FakeControllerContext(controller, cookies);
ActionResult result = controller.RemoteLogOn(username, password);

I'm not sure if this is the right way, but this is what we do, and it works.
Instead of directly using FormsAuthentication.SetAuthCookie, abstract it into an interface, e.g IFormsAuthenticationService, and implement as per regular.
Accept that in your MVC controllers where required, e.g:
public AccountController(IFormsAuthenticationService formsAuthenticationService)
{
_formsAuthenticationService = formsAuthenticationService; // should use DI here
}
public ActionResult LogOn(string username, string pw)
{
if (yourLogicWhichChecksPw)
_formsAuthenticationService.SetAuthCookie(username, false);
return RedirectToAction("Index");
}
Then in your unit-test, use something like Moq to fake out the interface.
var username = "blah";
var pw = "blah";
var fakesFormsAuth = new Mock<IFormsAuthenticationService>();
fakeFormsAuth.Verify(x => x.SetAuthCookie(username, false), Times.AtLeastOnce());
var controller = new AccountController(fakedFormsAuth.Object);
controller.LogOn(username, pw);
The reason for mocking this is because there is absolutely no need to unit-test Forms Authentication. It's a built-in, well tested and stable part of the ASP.NET framework. That's why we mock things where we don't care about the underlying implementation, instead we only test that certain conditions were met (it was called, exception was thrown, some variable was set, etc).
Test your own code, not the mechanics of .NET.
As for Stephen Walther's article, that's more for faking the RequestContext when certain code your testing expects data in the Request. Such as the User.Identity, Request.IsAuthenticated, Form variables, etc. That's where you need to fake the context, such as the following code:
public ActionResult Save(SomeModel)
{
var user = Request.User.Identity; // this will be null, unless you fake the context.
}

Related

How to mock Request.IsAjaxRequest() to true using FakeItEasy?

Following is the code snippet for which I want to write unit tests:
[HttpGet]
public ActionResult Edit(string id)
{
if (Request.IsAjaxRequest())
{
EditModel model = new EditModel();
.....
}
return View();
}
I want to write unit tests for this action where I can fake the result of Request.IsAjaxRequest() to true so that I can write tests for rest of the code of the action.
I have tried following but it doesn't work. _request.Headers is always empty, and Request.IsAjaxRequest() is always returning false:
[Fact]
public void Get_Edit_AjaxRequest_ExpectedActionCalled()
{
HttpRequestBase _request = A.Fake<HttpRequestBase>();
_request.Headers.Add("X-Requested-With", "XMLHttpRequest");
_controller.ControllerContext = A.Fake<ControllerContext>();
_controller.ControllerContext.HttpContext = _request;
A.CallTo(() => _controller.Request).Returns(_request);
var result = _controller.Edit(1) as RedirectToRouteResult;
}
I always get Request.IsAjaxRequest() as false. Any help on this much appreciated. Thanks
I managed to muddle past the compilation errors and use some information from Chapter 10 of FakeItEasy Succinctly, which is all about ASP.NET MVC.
Generally speaking, the ASP.NET MVC classes are not designed in a way to make them easily fakeable, but I have a test setup that causes IsAjaxRequest to return true. The two main hurdles were getting the controller to use the request object and to make sure that the request object was returning the headers we wanted.
The first part was not hard, but the second required us to have the request object use a concrete NameValueCollection. The faked one that it had been providing by default was not useful, because the right properties weren't virtual. Fortunately, using a real NameValueCollection did the trick.
Try this:
[Fact]
public void Get_Edit_AjaxRequest_ExpectedActionCalled_Blair()
{
HttpRequestBase _request = A.Fake<HttpRequestBase>();
// NameValueCollection is effectively unfakeable due to non-virtual properties,
// but a real one works just fine, so make sure the headers use one of those.
A.CallTo(() => _request.Headers).Returns(new NameValueCollection());
_request.Headers["X-Requested-With"] = "XMLHttpRequest";
var httpContext = A.Fake<HttpContextBase>();
A.CallTo(() => httpContext.Request).Returns(_request);
_controller.ControllerContext = new ControllerContext(
new RequestContext(httpContext, new RouteData()),
_controller);
var result = _controller.Edit(1) as RedirectToRouteResult;
}
Be warned that there will be lots of pitfalls like this in the MVC framework, and continuing to fake them may continue to be frustrating. You may find a more sustainable approach is to extract as much of your logic as is feasible out into plain old testable business classes that don't rely on the MVC framework.

How do I unit test a custom ActionFilter in ASP.Net MVC

So I'm creating a custom ActionFilter that's based mostly on this project http://www.codeproject.com/KB/aspnet/aspnet_mvc_restapi.aspx.
I want a custom action filter that uses the http accept headers to return either JSON or Xml. A typical controller action will look like this:
[AcceptVerbs(HttpVerbs.Get)]
[AcceptTypesAttribute(HttpContentTypes.Json, HttpContentTypes.Xml)]
public ActionResult Index()
{
var articles = Service.GetRecentArticles();
return View(articles);
}
The custom filter overrides the OnActionExecuted and will serialize the object (in this example articles) as either JSON or Xml.
My question is: how do I test this?
What tests do I write? I'm a TDD novice and am not 100% sure what I should be testing and what not to test. I came up with AcceptsTypeFilterJson_RequestHeaderAcceptsJson_ReturnsJson(), AcceptsTypeFilterXml_RequestHeaderAcceptsXml_ReturnsXml() and AcceptsTypeFilter_AcceptsHeaderMismatch_ReturnsError406().
How do I test an ActionFilter in MVC that is testing the Http Accept Headers?
Thanks.
You just need to test the filter itself. Just create an instance and call the OnActionExecuted() method with test data then check the result. It helps to pull the code apart as much as possible. Most of the heavy lifting is done inside the CsvResult class which can be tested individually. You don't need to test the filter on an actual controller. Making that work is the MVC framework's responsibility.
public void AcceptsTypeFilterJson_RequestHeaderAcceptsJson_ReturnsJson()
{
var context = new ActionExecutedContext();
context.HttpContext = // mock an http context and set the accept-type. I don't know how to do this, but there are many questions about it.
context.Result = new ViewResult(...); // What your controller would return
var filter = new AcceptTypesAttribute(HttpContentTypes.Json);
filter.OnActionExecuted(context);
Assert.True(context.Result is JsonResult);
}
I just stumbled upon this blog post which seems the right way to me. He uses Moq.
What this chap is doing is mocking the HTTPContext, but also we need to set up a ContentType in the request:
// Mock out the context to run the action filter.
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.ContentType).Returns("application/json");
var httpContext = new Mock<HttpContextBase>();
httpContext.SetupGet(c => c.Request).Returns(request.Object);
var routeData = new RouteData(); //
routeData.Values.Add("employeeId", "123");
var actionExecutedContext = new Mock<ActionExecutedContext>();
actionExecutedContext.SetupGet(r => r.RouteData).Returns(routeData);
actionExecutedContext.SetupGet(c => c.HttpContext).Returns(httpContext.Object);
var filter = new EmployeeGroupRestrictedActionFilterAttribute();
filter.OnActionExecuted(actionExecutedContext.Object);
Note - I have not tested this myself.

MVC view unit test visibility of element based on authentication

I'm writing an MVC app.
If I have a piece of code:
#if (User.IsInRole("Administrator")) {
#Html.DropDownListFor(...)
}
So the dropdownlistfor is only visible or even there for administrators.
How do you unit test that this is happening?
This is what I would do:
Create a stub for the User object (of type IPrincipal)
Create a stub for the Request object (of type HttpRequestBase) with the stubbed User object
Inject the stubbed request object to your request context.
See example below (using Rhino mocks)
var CurrentUser = MockRepository.GenerateStub<IPrincipal>();
CurrentUser.Stub(u => u.IsInRole("Administrator")).Return(true);
var context = MockRepository.GenerateStub<HttpContextBase>();
var requestContext = new RequestContext(context, new RouteData());
var request = MockRepository.GenerateStub<HttpRequestBase>();
context.User = CurrentUser;
context.Stub(c => c.Request).Return(request);
controller = new YourController();
controller.ControllerContext = new ControllerContext(requestContext, controller);
var view = controller.DoAction() as ViewResult;
That would help you setup the controller and the current user with all required roles.
I personally wouldn't unit test the view as it is supposed to be thin and dump. Most of your stuff should happen in the controller/business layer.
If you want to unit test your views, I would suggest checking out the Razor Single File Generator. This will allow you to pre-compile your views, and to create unit tests as well.

Mocking a session in MVC 3

I am attempting to mock a controller's Session variable using Moq. What I keep running into is that the Session is visible from the test, but null inside the actual controller.
Test code:
[TestMethod]
public void SessionTest()
{
var controller = new BaseController();
var controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(cc => cc.HttpContext.Session["user"]).Returns(new User());
controller.ControllerContext = controllerContext.Object;
User currentUser = controller.CurrentUser; //fails (throws NullReferenceException)
User currentUser = (User)controller.Session["test"]; //works
}
Controller code:
public User CurrentUser
{
get
{
return (User)Session["user"]; //HttpContext is null at this point
}
}
The code above follows the most common pattern I've seen (several SO postings, the MVC 2 in Action Ch. 7 example, etc.), but still doesn't work. Inside the controller, the ControllerContext is null, as is the HttpContext. What am I doing wrong?
It must have been a configuration issue caused by adding a unit test project to an already-existing project, because I just created a new MVC 3 project, checked the 'Create Unit Test Project', added all of my controllers, models, etc. from the old project to the new one, and it's working just fine.

Unit testing a controller in ASP.NET MVC 2 with RedirectToAction

I have a controller that implements a simple Add operation on an entity and redirects to the Details page:
[HttpPost]
public ActionResult Add(Thing thing)
{
// ... do validation, db stuff ...
return this.RedirectToAction<c => c.Details(thing.Id));
}
This works great (using the RedirectToAction from the MvcContrib assembly).
When I'm unit testing this method I want to access the ViewData that is returned from the Details action (so I can get the newly inserted thing's primary key and prove it is now in the database).
The test has:
var result = controller.Add(thing);
But result here is of type: System.Web.Mvc.RedirectToRouteResult (which is a System.Web.Mvc.ActionResult). It doesn't hasn't yet executed the Details method.
I've tried calling ExecuteResult on the returned object passing in a mocked up ControllerContext but the framework wasn't happy with the lack of detail in the mocked object.
I could try filling in the details, etc, etc but then my test code is way longer than the code I'm testing and I feel I need unit tests for the unit tests!
Am I missing something in the testing philosophy? How do I test this action when I can't get at its returned state?
I am using MVC2 RC2 at the moment and the answer from rmacfie didn't quite work for me but did get me on the right track.
Rightly or wrongly I managed to do this in my test instead:
var actionResult = (RedirectToRouteResult)logonController.ForgotUsername(model);
actionResult.RouteValues["action"].should_be_equal_to("Index");
actionResult.RouteValues["controller"].should_be_equal_to("Logon");
Not sure if this will help someone but might save you 10 minutes.
There is MVC Contrib TestHelper that are fantastic for testing most of the ActionResult
You can get it here:
http://mvccontrib.codeplex.com/wikipage?title=TestHelper
Here is an example of the syntax:
var controller = new TestController();
controller.Add(thing)
.AssertActionRedirect()
.ToAction<TestController>(x => x.Index());
To test if the data has been persisted successfully, you should maybe ask your database directly, I don't know if you're using an ORM or something, but you should do something to get the last insterted item in your database, then compare with the value you provided to your Add ActionResult and see if this is ok.
I don't think that testing your Details ActionResult to see if your data is persisted is the right approach. That would not be an unit test, more a functional test.
But you should also unit test your Details method to make sure that your viewdata object is filled with the right data coming from your database.
You seem to be doing way too much for a unit test. The validation and data access would typically be done by services that you call from the controller action. You mock those services and only test that they were called properly.
Something like this (using approximate syntax for Rhino.Mocks & NUnit):
[Test]
public void Add_SavesThingToDB()
{
var dbMock = MockRepository.GenerateMock<DBService>();
dbMock.Expect(x => x.Save(thing)).Repeat.Once();
var controller = new MyController(dbMock);
controller.Add(new Thing());
dbMock.VerifyAllExpectations();
}
[Test]
public void Add_RedirectsAfterSave()
{
var dbMock = MockRepository.GenerateMock<DBService>();
var controller = new MyController(dbMock);
var result = (RedirectToRouteResult)controller.Add(new Thing());
Assert.That(result.Url, Is.EqualTo("/mynew/url"));
}
I have a static helper method that tests redirection.
public static class UnitTestHelpers
{
public static void ShouldEqual<T>(this T actualValue, T expectedValue)
{
Assert.AreEqual(expectedValue, actualValue);
}
public static void ShouldBeRedirectionTo(this ActionResult actionResult, object expectedRouteValues)
{
RouteValueDictionary actualValues = ((RedirectToRouteResult)actionResult).RouteValues;
var expectedValues = new RouteValueDictionary(expectedRouteValues);
foreach (string key in expectedValues.Keys)
{
Assert.AreEqual(expectedValues[key], actualValues[key]);
}
}
}
Then creating a redirection test is very easy.
[Test]
public void ResirectionTest()
{
var result = controller.Action();
result.ShouldBeRedirectionTo(
new
{
controller = "ControllerName",
action = "Index"
}
);
}

Resources