Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Unit Testing Secure Controller Actions with Moq

DZone's Guide to

Unit Testing Secure Controller Actions with Moq

·
Free Resource
One of the hardest things to unit test in MVC is security. Security is tough to test because there is a lot of setup involved in mocking the HttpContext, the Principal and the Identity. For example, in WeBlog I am using the following code in the Edit Post action.
Post post = Repository.FirstOrDefault<Post>(x => x.ID == id);

if (post == null) return View("NotFound");
if (!HttpContext.User.CanEditPost(post)) return View("PermissionDenied");
In order to make sure this code works properly I need to test it with an authorized and unauthorized user. Unfortunately, the HttpContext.User will not automatically be created for your tests so you have to mock one for each test that your perform. So lets start this journey by reviewing the code required to mock the HttpContext using the popular opensource library Moq. This code is a combination of code I discovered on Stackoverflow and Scott Hanselman’s MvcMockHelpers:
public static Mock<HttpContextBase> MockContext( IPrincipal principal = null )
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();

context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);

if( principal != null )
context.Setup(ctx => ctx.User).Returns(principal);

return context;
}
If you are familiar with Scott’s MvcMockHelpers then you will probably notice that I modified the method signature to include an optional IPrincipal object. If the value is not null then I set the HttpContext.User. I also modified the SetFakeControllerContext extension method to take the IPrincipal parameter as well.
public static void SetFakeControllerContext(this Controller controller, IPrincipal principal = null )
{
var httpContext = FakeHttpContext( principal );
ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
controller.ControllerContext = context;
controller.Url = new UrlHelper( context.RequestContext, new RouteCollection() );
}
Now that we have the major pieces in place, its just a matter of creating a Fake user and passing it into the SetFakeControllerContext method. To create the IPrincipal, I am using these two Mock classes which were posted by Razzie on Stackoverflow:
public class MockPrincipal : IPrincipal
{
private IIdentity _identity;
private readonly string[] _roles;

public MockPrincipal(IIdentity identity, string[] roles)
{
_identity = identity;
_roles = roles;
}

public IIdentity Identity
{
get { return _identity; }
set { this._identity = value; }
}

public bool IsInRole(string role)
{
if (_roles == null)
return false;
return _roles.Contains(role);
}
}
public class MockIdentity : IIdentity
{
private readonly string _name;

public MockIdentity(string userName) {
_name = userName;
}

public override string AuthenticationType
{
get { throw new System.NotImplementedException(); }
}

public override bool IsAuthenticated
{
get { return !String.IsNullOrEmpty(_name); }
}

public override string Name
{
get { return _name; }
}
}

Putting the pieces together


So based on the code above I made two tests which create the Mock users and test their ability to edit a post. The first test should return the “PermissionDenied” View. Therefore, I create a new MockPrincipal which is in the Subscriber role. The Subscriber role is a read-only account which should prohibit the user from editing anything in the application.
[TestMethod]
public void EditActionShouldReturnPermissionDeniedViewForUnauthorizedUser() {
var principal = new MockPrincipal(new MockIdentity("Guest"), new string[] { Role.Subscriber });
var postController = new PostController(_repository);
postController.SetFakeControllerContext(principal);

var result = postController.Edit(_fakePost.ID) as ViewResult;
Assert.AreEqual("PermissionDenied", result.ViewName);
}
Next, we perform a similar test with an Admin user which should be able to edit any post.
[TestMethod]
public void EditActionShouldReturnValidViewForAuthorizedUser()
{
var principal = new MockPrincipal(new MockIdentity("Admin"), new string[] {Role.Admin});
var postController = new PostController(_repository);
postController.SetFakeControllerContext(principal);

var result = postController.Edit(_fakePost.ID) as ViewResult;
Assert.IsInstanceOfType(result.ViewData.Model, typeof(PostFormViewModel));
}
And the end result is a successful test!

Topics:

Published at DZone with permission of Michael Ceranski, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}