1

I am writing some tests for an AuthorizeAttribute class I wrote for my Web API Controllers. I am trying to mock up everything I need to be able to set the following vars within my class AuthorizeAttributeTotalAccess:

  1. actionName = filterContext.ActionDescriptor.ActionName;
  2. controllerName = filterContext.ControllerContext.ControllerDescriptor.ControllerName;
  3. claimsPrincipal = HttpContext.Current.User as ClaimsPrincipal;

NOTE: I am not married to HttpContext.Current and if there is another Context thaat will always have it, let me know.

My Test, so far, looks like:

using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
using System.Web.Http;
using System.Web.Http.Controllers;
using BI.Security.Library.Authorization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;


namespace AppTest
{
    [TestClass]
    public class AttributeAuthorize
    {
        private AuthorizeAttributeTotalAccess _filter;
        private HttpActionContext _actionContext;
        private IPrincipal _originalPrincipal;

        [TestInitialize]
        public void SetUp()
        {
            var attributes = new Collection<AllowAnonymousAttribute>();

            var controllerDescriptor = Substitute.For<HttpControllerDescriptor>();
            controllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Returns(attributes);
            controllerDescriptor.ControllerName = "Template";

            var controllerContext = new HttpControllerContext
            {
                Request = new HttpRequestMessage(),
                ControllerDescriptor = controllerDescriptor
            };

            // HOW DO I SET ACTIONNAME HERE???
            var actionDescriptor = Substitute.For<HttpActionDescriptor>();
            actionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Returns(attributes);

            _actionContext = new HttpActionContext(controllerContext, actionDescriptor);

            _originalPrincipal = Thread.CurrentPrincipal;
            _filter = new AuthorizeAttributeTotalAccess();
        }

        [TestCleanup]
        public void TearDown()
        {
            Thread.CurrentPrincipal = _originalPrincipal;
        }

        [TestMethod]
        public void Returns_unauthorized_response_if_user_is_not_authenticated()
        {
            _filter.OnAuthorization(_actionContext);

            Assert.IsNotNull(_actionContext.Response);
            Assert.AreSame(_actionContext.Response.StatusCode, HttpStatusCode.Unauthorized);
        }

        [TestMethod]
        public void Returns_unauthorized_response_if_user_is_authenticated_but_not_in_system_roles()
        {
            Thread.CurrentPrincipal = GetTestUser("admin");

            _filter.OnAuthorization(_actionContext);

            Assert.AreSame(_actionContext.Response.StatusCode, HttpStatusCode.Unauthorized);
        }

        [TestMethod]
        public void Short_circuits_request_if_user_is_authenticated_and_in_system_roles()
        {
            Thread.CurrentPrincipal = GetTestUser("admin", "ApiWriteUser");

            _filter.OnAuthorization(_actionContext);

            Assert.IsNull(_actionContext.Response);
        }

        private IPrincipal GetTestUser(params string[] roles)
        {
            var identity = new ClaimsIdentity(new[] {new Claim(ClaimTypes.Name, "Test User")}, "password");
            roles.ToList().ForEach(a => identity.AddClaim(new Claim(ClaimTypes.Role, a)));


            var claimsPrincipal = new ClaimsPrincipal(identity);

            _actionContext.RequestContext.Principal = claimsPrincipal;

            return claimsPrincipal;
        }
    }
}

What am I missing?

Again, how do I:

  1. Set the ActionName so it is available in the class to be tested?
  2. Set the HttpContext.Current so I can see the User?
vcsjones
  • 128,004
  • 28
  • 283
  • 274
Keith Barrows
  • 23,420
  • 26
  • 78
  • 127
  • For what it's worth, rather than using `HttpContext.Current.User` I would instead use `_actionContext.RequestContext.Principal`. This is easier to fake. – vcsjones Sep 16 '15 at 19:51
  • @vcsjones, how do you fake _actionContext.RequestContext.Principal? – Daniel Qiu Jan 11 '16 at 04:00

1 Answers1

2

Set the ActionName so it is available in the class to be tested?

I'm not quite an expert on NSubstitute (read: I've never used it), but since your HttpActionDescriptor is a stub / mock, you should be able to stub the return value:

actionDescriptor.ActionName.Return("BLAH");

According to NSubstitute's documentation, that's how you do a return value of a property.

Set the HttpContext.Current so I can see the User?

HttpContext.Current has a setter, so you should be able to set it to a fake context. Other Stack Overflow answers demonstrate how to do this.

Community
  • 1
  • 1
vcsjones
  • 128,004
  • 28
  • 283
  • 274