0

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);
GEOCHET
  • 21,119
  • 15
  • 74
  • 98
davidson1988
  • 51
  • 3
  • 7

1 Answers1

9

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.
}
RPM1984
  • 72,246
  • 58
  • 225
  • 350
  • 2
    This is the closest I've come to something I can actually understand since I started looking for how to mock SetAuthCookie yesterday. Thanks for this. – dartacus Aug 02 '12 at 13:17