How to create a unit test that tests that a page requires authorization in mvc5 - asp.net-mvc

I am trying to work out how to write a unit test that will test that a controllers authorization is working. IE a user that is not logged in can't access the page. Does anybody know how to go about this? I am having trouble finding examples.
something like this (Pseudo Code)
[TestMethod]
public void Get_Auth_Page()
{
be_a_user_thats_not_logged_in = true;
// Arrange
MyController controller = new MyController();
// Act
var result = controller.Index();
// Assert
if(result.httpstatus == 403)
Assert.True();
}

If you are simply decorating your action method with [Authorize], you can just have a test that asserts the existence of the attribute:
[TestMethod]
public void Index_action_requires_authentication()
{
// If Index is overloaded, you might need to filter by argument list
MethodInfo indexMethod = typeof(MyController).GetMethod("Index");
bool requiresAuthentication =
Attribute.IsDefined(indexMethod, typeof(AuthorizeAttribute));
Assert.IsTrue(requiresAuthentication);
}
Obviously you aren't testing the Authorize implementation here, but it does serve to both document and to protect against developers accidentally removing it.
If you are running custom code, then you are probably returning a HttpStatusCodeResult, so you can just check for that:
public void Index_action_requires_authentication()
{
ActionResult result = new MyController().Index();
HttpStatusCodeResult statusCodeResult = result as HttpStatusCodeResult;
Assert.IsNotNull(statusCodeResult);
Assert.AreEqual(403, statusCodeResult.StatusCode);
}
If you are manually writing to the HttpResponse (Response.StatusCode or Response.Headers), then you'll need to mock the HttpContextBase as others have described.

Related

MVC Unit testing [Authorize(Roles="Role")]

So I have been googling this for a couple of hours and I am yet to find a working solution.
Here are a couple of questions I have found that paint the picture of what I've been doing but none give me a working answer.
How do I unit test a controller method that has the [Authorize] attribute applied?
Unit testing ASP.Net MVC Authorize attribute to verify redirect to login page
What I am trying to do is to write a unit that that checks the [Authorise(Roles="Role")] attribute on my controller actually allows/denies access to the controller based on the current user belonging/not belonging to a specific role.
The code below always returns the view even when I set IsInRole to false hence I figure it is ignoreing the Authorise attribute.
[TestMethod]
public void Auth_User_Can_Access()
{
//this test mocks a user and submits it as part of the context to the controller
//Arrange
Mock<IPrincipal> mockP = new Mock<IPrincipal>();
mockP.SetupGet(p=>p.Identity.Name).Returns("UnitTesting");
mockP.Setup(p=>p.IsInRole("Role")).Returns(false); //"Role" is not the actual role name.
Mock<ControllerContext> mockC = new Mock<ControllerContext>();
mockC.SetupGet(p=>p.HttpContext.User).Returns(mockP.Object);
mockC.SetupGet(p=>p.HttpContext.Request.IsAuthenticated).Returns(true);
AppsController target = new AppsController(mock.Object);
target.ControllerContext = mockC.Object;
// Act
ViewResult result = target.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
}
I'm clearly missing something here.
For completeness here is the start of my Controller code also
[Authorize(Roles = "Role")]
public class AppsController : Controller
{
private IAppRepository db;
public AppsController (IAppRepository appRepository)
{
db = appRepository;
}
// GET: Apps
public ViewResult Index()
{
return View(db.Apps.ToList());
}
You can write your unit test with the help of Xania.AspNet.Simulator
new AppsController(appRepo).Action(c => c.Index())
.Authenticate("user1", new []{"Role"})
.Authorize().Should().BeNull(); // authorized
new AppsController(appRepo).Action(c => c.Index())
.Authenticate("user1", new []{"Dummy"})
.Authorize().Should().BeOfType<HttpUnauthorizedResult>(); // not authorized
for more information please refer to http://www.codeproject.com/Tips/850277/ASP-NET-MVC-End-to-End-Integration-Testing

Unit test an Action with Recaptcha

How I can test the following Action of the controller:
public ActionResult Edit(User usr)
{
if (!Microsoft.Web.Helpers.ReCaptcha.Validate(ConfigurationManager.AppSettings["reCaptchaPrivate"].ToString()))
{
ModelState.AddModelError("reCaptcha", PPRR.App_LocalResources.Global.ErrorFillReCaptcha);
return PartialView("Wrong", usr);
}
if (ModelState.IsValid)
{code..... }}
I would start by abstracting the Captcha validation code:
public interface ICaptchaValidator
{
bool Validate();
}
and then have my controller look like this:
public class FooController: Controller
{
private readonly ICaptchaValidator _validator;
public FooController(ICaptchaValidator validator)
{
_validator = validator;
}
public ActionResult Edit(User usr)
{
if (!_validator.Validate())
{
ModelState.AddModelError("reCaptcha", PPRR.App_LocalResources.Global.ErrorFillReCaptcha);
return PartialView("Wrong", usr);
}
...
}
}
Now you have weaken the coupling between your controller and the way those captchas are validated. That's a good thing as it makes your controller action far easier to unit test. We have successfully made our controller independent of the actual way validation is implemented.
Now just pick a mocking framework such as Rhino Mocks, Moq, NSubstitute and in your unit test inject a stubbed validator into this controller so that you can define behaviors on it.
Personally I would recommend you MvcContrib.TestHelper (which is based on Rhino Mocks) to test your ASP.NET MVC applications. It has many built-in goodies for mocking the HttpContext and make unit testing easy.
Here's an example of how the validation failure case could be tested:
[TestMethod]
public void FooController_Edit_Action_Should_Return_The_Wrong_Partial_If_Captcha_Validation_Fails()
{
// arrange
var validatorStub = MockRepository.GenerateStub<ICaptchaValidator>();
var sut = new HomeController(validatorStub);
var user = new User();
validatorStub.Stub(x => x.Validate()).Return(false);
// act
var actual = sut.Edit(user);
// assert
actual
.AssertPartialViewRendered()
.ForView("Wrong")
.WithViewData<User>()
.Equals(user);
Assert.IsFalse(sut.ModelState.IsValid);
}
As an alternative to Darin's answer, I've previously used a custom ActionFilter that processes the captcha and adds the error to the ModelState. It worked really well and meant that the captcha code wasn't part of the action method itself.

Response Object is a null reference in my Controller's action method

I'm developing a webapp using ASP.NET MVC and C#. And I'm creating a unit test for this webapp using NUnit and Rhino Mock. My problem is that I have a Response object in my controller's action method and when I execute my unit test my test is failing because the Response object is a null reference.
Do I need to separate this Response object call in my actions or there is a better way to resolve this?
public ActionResult Login( string user, string password )
{
Response.Cookies[ "cookie" ].Value = "ck";
...
return View();
}
Please advise.
Many thanks.
What the controller really lacks is its HttpContext. In a test method it should be added explicitly if needed:
[Test]
public void TestMethod()
{
// Assume the controller is created once for all tests in a setup method
_controller.ControllerContext.HttpContext = new DefaultHttpContext();
var result = _controller.Login("username", "verySaf3Passw0rd");
// Asserts here
}
This is one of the annoying points where ASP.NET MVC is not as testable and loosely coupled as it could be. See this question for some suggestions how to mock the HTTP context objects.
I ended up creating a real response that my mock context returns like this...
Mock<HttpSessionStateBase> mockSession;
Mock<ControllerContext> mockContext;
Mock<ISessionProvider> mockSessionProvider;
HttpResponse testResponse;
MyController controller;
[TestInitialize]
public void Initialize()
{
testResponse = new HttpResponse(TextWriter.Null);
mockContext = new Mock<ControllerContext>();
mockSession = new Mock<HttpSessionStateBase>();
mockContext.Setup(x => x.HttpContext.Session).Returns(mockSession.Object);
mockContext.Setup(x => x.HttpContext.Response).Returns(new HttpResponseWrapper(testResponse));
controller = new MyController();
controller.ControllerContext = mockContext.Object;
}

How to unit test a custom actionresult

I'm trying to unit test a custom action result. I recently watched Jimmy Bogard's excellent MvcConf video ("put your controllers on a diet") http://www.viddler.com/explore/mvcconf/videos/1/ and have started to try and implement some custom action results. I've managed that without a problem, the ActionResult works fine at runtime but I'm having trouble trying to unit test them.
Unfortunately in the code download there are no unit tests for Jimmy's custom action methods... which make me wonder.
I realise that action methods just return instances of the ActionResult types and its the MVC framework that actually calls the ExecuteResult method, which of course is not available when running the unit test. So my unit test is now just creating an instance of my custom ActionResult and I then call ExecuteResult.
Unfortunatley in the ExecuteResult method of my custom ActionResult it is also calling the ExecuteResult method of a ViewResult that I passed it. At that point it blows up. How should I be mocking/stubbing these things to get my unit test working?
public class SendToAFriendActionResult : ActionResult
{
public const string INVALID_CAPTCHA = "You don't appear to have filled out the two words from the security image correctly to prove you're a human. Please try again.";
public const string INVALID_MODEL_STATE = "You don't appear to have filled out all the details correctly. Please try again.";
public const string CONTACT_FAIL = "Unfortunately we experiend a problem sending the link. Please try again later.";
public const string SEND_TO_A_FRIEND_FAIL_KEY = "ContactFail";
private RedirectResult _success;
private ViewResult _failure;
private readonly SendToAFriendModel _model;
private readonly bool _captchaValid;
private readonly MessageBuilderServiceBase _mbs;
public RedirectResult Success
{
get { return _success; }
set { _success = value; }
}
public ViewResult Failure
{
get { return _failure; }
set { _failure = value; }
}
public SendToAFriendActionResult(RedirectResult success, ViewResult failure, SendToAFriendModel model, bool captchaValid, MessageBuilderServiceBase mbs)
{
_success = success;
_failure = failure;
_model = model;
_captchaValid = captchaValid;
_mbs = mbs;
}
public override void ExecuteResult(ControllerContext context)
{
if (!_captchaValid)
{
Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_CAPTCHA;
// On reaching this point I receive the error
// Object reference not set to an instance of an object
// as the MVC framework calls FindView
Failure.ExecuteResult(context);
return;
}
if (!context.Controller.ViewData.ModelState.IsValid)
{
Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = INVALID_MODEL_STATE;
Failure.ExecuteResult(context);
return;
}
_mbs.RecipientEmailAddress = _model.EmailRecipient;
_mbs.SendersName = _model.SendersName;
_mbs.Url = _model.URL;
var result = _mbs.sendMessage();
if (!result)
{
Failure.TempData[SEND_TO_A_FRIEND_FAIL_KEY] = CONTACT_FAIL;
Failure.ExecuteResult(context);
return;
}
Success.ExecuteResult(context);
}
}
Here's the start of my unit test ...
IMessageService _emailMessageSerivce;
IGalleryRepository _repository;
var stfModel = new SendToAFriendModel
{
SendersName = "Someone",
URL = "http://someurl.com",
EmailRecipient = "a-friend#somewherelse.com"
};
var failure = new ViewResult() {ViewName ="SendToFriend"};
const bool captchaValid = false;
var fakeControlllerContext = MockRepository.GenerateStub<ControllerContext>(null);
var stf = new SendToAFriendActionResult(null, failure, stfModel, captchaValid, null);
stf.ExecuteResult(fakeControlllerContext);
I've put comments in the SUT to show were the problem occurs.
I know I should be stubbing/mocking somehow but I just can't seem to resolve this.
From ASP.NET MVC 2 In Action (coauthored by Jimmy Bogard):
By taking that hard-to-test code out
of an action and putting it into the
Execute method of an action result,
you ensure that the actions become
significantly easier to unit-test.
That’s because when you unit-test an
action, you assert the type of action
result that the action returns and the
state of the action result. The
Execute method of the action result
isn’t executed as part of the unit
test.
Unit tests are designed to isolate behavior and concerns. You're mixing concerns by calling ExecuteResult from within your custom Action. Instead, I would have the SendToAFriendActionResult return the actual ActionResult (Failure or Success):
public ActionResult GetAction(..)
{
ActionResult result;
//logic here to determine which ActionResult to return
return result;
}
In your Controller:
public ViewResult SendToAFriend()
{
return SendToAFriendActionResult(null, failure, stfModel, captchaValid, null)
.GetAction();
}
This method will allow the MVC framework to do its job and isolates those concerns outside your custom ActionResult. Your test should assert that the correct type of Action, failure or success, is returned based on the parameters you set going in.

What is needed in the HttpContext to allow FormsAuthentication.SignOut() to execute?

I am trying to write a unit test for our log out method. Among other things it FormsAuthentication.SignOut(). However, it throws a System.NullReferenceException.
I've created a mock; HttpContext (using Moq), but it is obviously missing something.
My mock context contains:
A mocked HttpRequestBase on Request
A mocked HttpResponseBase on Response
With a HttpCookieCollection on Request.Cookies and another on Response.Cookies
A mocked IPrincipal on User
I am aware I could go the wrapper route and inject an empty FormsAuth wrapper object in it's place, but I would really like to avoid the 3 additional files just to fix one line of code. That and I am still curious for an answer
So my question is "What is needed in the HttpContext to allow FormsAuthentication.SignOut() to execute."
The NullReferenceException in this case is actually being thrown by the call:
current.Request.Browser["supportsEmptyStringInCookieValue"]
You can test this assertion by calling:
HttpContext.Current.Request.Browser.SupportsEmptyStringInCookieValue
...which will also return the NullReferenceException. Contrary to the accepted answer, if you attempt to call:
CookielessHelperClass.UseCookieless(current, false, CookieMode)
...from the immediate window, this will return without error.
You can fix the exception like this:
HttpContext.Current.Request.Browser = new HttpBrowserCapabilities() { Capabilities = new Dictionary<string, string> { { "supportsEmptyStringInCookieValue", "false" } } };
...and the FormsAuthentication.SignOut() call will now succeed.
You can always wrap FormsAuthentication.SignOut() into another method and stub / mock it.
Create IFormsAuthenticationWrap interface.
public interface IFormsAuthenticationWrap
{
void SignOut();
}
Create wrap class that implements IFormsAuthenticationWrap
public class FormsAuthenticationWrap : IFormsAuthenticationWrap
{
public void SignOut()
{
FormsAuthentication.SignOut();
}
}
Your calling class is going to look something like this:
public class LogOutClass
{
private readonly IFormsAuthenticationWrap _formsAuthentication;
public LogOutClass() : this (new FormsAuthenticationWrap())
{
}
public LogOutClass(IFormsAuthenticationWrap formsAuthentication)
{
_formsAuthentication = formsAuthentication;
}
public void LogOutMethod()
{
// Code before SignOut
_formsAuthentication.SignOut();
// Code after SignOut
}
}
Now let's get to our test. You can stub / mock with Moq but I'm going to show here how you can do it manually.
Create your stub / mock class:
public class FormsAuthenticationStub : IFormsAuthenticationWrap
{
public void SignOut()
{
}
}
And the last write the test:
[TestMethod]
public void TestLogOutMethod()
{
var logOutClass = new LogOutClass(new FormsAuthenticationStub());
logOutClass.LogOutMethod();
}
Here's the code for signout.
public static void SignOut()
{
Initialize();
HttpContext current = HttpContext.Current;
bool flag = current.CookielessHelper.DoesCookieValueExistInOriginal('F');
current.CookielessHelper.SetCookieValue('F', null);
if (!CookielessHelperClass.UseCookieless(current, false, CookieMode) || current.Request.Browser.Cookies)
{
string str = string.Empty;
if (current.Request.Browser["supportsEmptyStringInCookieValue"] == "false")
{
str = "NoCookie";
}
HttpCookie cookie = new HttpCookie(FormsCookieName, str);
cookie.HttpOnly = true;
cookie.Path = _FormsCookiePath;
cookie.Expires = new DateTime(0x7cf, 10, 12);
cookie.Secure = _RequireSSL;
if (_CookieDomain != null)
{
cookie.Domain = _CookieDomain;
}
current.Response.Cookies.RemoveCookie(FormsCookieName);
current.Response.Cookies.Add(cookie);
}
if (flag)
{
current.Response.Redirect(GetLoginPage(null), false);
}
}
Looks like you need a CookielessHelperClass instance. Too bad it's internal and sealed - there's no way to mock it unless you're using TypeMock. +1 for wrapper suggestions :)
The wrapper is the clean way to go.
You mentioned in a comment that "this is going to be quite a big application", that's another argument to use the wrapper not the opposite. In a big application you want to have clear dependencies, and you want tests to be done easily.
You are trading clean dependencies that can be easily injected over obscure dependencies to the internal workings of asp.net in your tests.
On a different note: Use Reflector. I honestly don't know the inner dependencies of this specific part of asp.net, but you can clear any doubts with reflector.
Don't mock HttpContext, use a real one in your tests. This way you don't have to mock all these Http* stuff. You can use Ivonna and test your method directly, without mocking all these dependencies and getting mysterious exceptions.

Resources