【问题标题】:Unit test Custom authorize attribute单元测试自定义授权属性
【发布时间】:2017-05-15 13:50:11
【问题描述】:

我编写了一个自定义声明授权属性,我想对我编写的代码进行单元测试,但在 SO 上找不到我要查找的内容。

例如,这是我自定义的授权属性类:

using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using IPD2.MVC.Interfaces.Providers;
using IPD2.MVC.Providers;

namespace IPD2.MVC.Attribute 
{
    public class ClaimsAuthorizeAttribute : AuthorizeAttribute
    {
        private readonly string _claimValue;
        private readonly string _claimType;
        private readonly ITokenProvider _tokenProvider;

        public ClaimsAuthorizeAttribute(string type, string value)
        {
            _claimType = type;
            _claimValue = value;
            _tokenProvider = new TokenProvider();
        }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            var jwt = _tokenProvider.ApiToken as JwtSecurityToken;
            if (jwt == null)
            {
                HandleUnauthorizedRequest(filterContext);
            }
            else
            {
                var claim = jwt.Claims.FirstOrDefault(expr => expr.Value == _claimValue);
                var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
                if (authCookie != null)
                {
                    var formsAuthenticationTicket = FormsAuthentication.Decrypt(authCookie.Value);
                    if (formsAuthenticationTicket != null && !formsAuthenticationTicket.Expired)
                    {
                        var roles = formsAuthenticationTicket.UserData.Split(',');
                        HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new FormsIdentity(formsAuthenticationTicket), roles);
                    }
                }

                if (claim != null)
                {
                    base.OnAuthorization(filterContext);
                }
                else
                {
                    HandleUnauthorizedRequest(filterContext);
                }
            }

        }
    }
}

测试代码

  public class GivenCallingClaimAuthorizationAttribute : SpecsFor<ClaimsAuthorizeAttribute>
    {
        //HttpActionContext actionContext;
        //IPrincipal originalPrincipal;

        protected override void Given()
        {

            SUT = new ClaimsAuthorizeAttribute(ClaimTypes.Role, "ImNotAllowedToUseController :(");

        }

        public class WhenUserIsNotAllowedToAccessController : GivenCallingClaimAuthorizationAttribute
        {
            protected override void When()
            {
                SUT.OnAuthorization(
                    new AuthorizationContext()
                );
            }
        }

        [Test]
        public void ThenAssertSomethingBasedOnCodeInTest()
        {
            //todo: some assert
        }
    }

我已经使用 SpecsFor BDD 框架编写了这个基本测试类,但我不确定它需要什么才能成功测试它。

关于如何测试它的任何想法?如您所见,我正在测试类本身而不是具有该属性的控制器。我不确定测试这个的好方法。

【问题讨论】:

    标签: asp.net-mvc unit-testing bdd authorize-attribute


    【解决方案1】:

    如果您想做 BDD,请考虑您班级的行为。它应该能够做什么样的事情?

    例如,也许它应该:
    - 授权当前用户
    - 过滤票已过期的用户
    - 为授权用户分配正确的角色
    - 等等。

    假设我们要看看其中的第二个,因为它很有趣。我会问你,“你能给我举个例子说明票什么时候过期吗?”

    然后你说,“是的,超时时间是 500 毫秒,所以任何比这更早的都过期了。”或者你说,“这些是足球比赛的门票,每年都会派发。” (我不知道票是什么,但这次谈话会帮助我解决这个问题。)

    那么我们可以写一个例子:

    Given we've got a handler for unauthorized tickets
    Given Fred's ticket expired on 2017-05-14  
    And it's now 2017-05-14   
    When we try to authenticate Fred  
    Then Fred should not be authenticated  
    And the handler should be given Fred's details
    

    接下来我喜欢将这些放在代码中的 cmets 中,以“应该”作为测试的标题。之后,只需设置上下文(在 Given 中),将其传递给您的代码以执行(When)并验证结果(The Then)。

    但是,您可以看到您的 Specs 框架只允许一个给定的!这不是很好,因为我已经确定了几个需要不同上下文的场景。如果你按照你的使用方式使用 Specs,你将不得不为我的每个场景制作一个类,而且它们不会很容易阅读!

    我强烈建议改用第二种样式,并从 cmets 中的 Given、When、Then 开始。

    问题是,与全系统 BDD 场景相比,单元测试的步骤并没有真正重用,在全系统 BDD 场景中,通常多次调用上下文以获得不同的功能。一个代码单元(或类)倾向于履行一项职责,或者以这种方式重构,并且它的上下文不会因为 Mocks 而渗透到其他类中。

    所以,我发现在 cmets 中有 Given、When 和 Then 就足够了。

    因此,我建议您切换到我可以在此页面(底部)中看到的“老派”做事方式,然后从 cmets 开始。看看这对你有什么影响。

    顺便说一句,如果你真的想正确地做 BDD,那么你想驱动用它的行为示例来推动开发,并逐步填充代码以使这些示例工作,而不是把它们写下来。 "Growing Object Oriented Software, Guided By Tests"这本书对你很有帮助。

    【讨论】:

      猜你喜欢
      • 2011-07-01
      • 1970-01-01
      • 2015-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多