代码之家  ›  专栏  ›  技术社区  ›  CyclingFreak

CodeAccessSecurityAttribute的模拟自定义实现

  •  0
  • CyclingFreak  · 技术社区  · 7 年前

    我有一个自定义的 CodeAccessSecurityAttribute 即连接外部源以进行验证。

    [Serializable]
    [AttributeUsage(AttributeTargets.Method)]
    public class IsAuthorizedAttribute : CodeAccessSecurityAttribute
    {
        private static readonly PrincipalPermission Allowed = new PrincipalPermission(PermissionState.None);
        private static readonly PrincipalPermission NotAllowed = new PrincipalPermission(PermissionState.Unrestricted);
    
        public string EntityObject { get; set; }
        public string Field { get; set; }
        public char Expected { get; set; }
    
        public IsAuthorizedAttribute(SecurityAction action)
                : base(action)
        {
            //setup
        }
    
        public override IPermission CreatePermission()
        {
            return IsAuthorised(EntityObject, Field, Expected, ServicesConfiguration) ? Allowed : NotAllowed;
        }
    
        private static bool IsAuthorised(string entityObject, string field, char expected, ServicesConfiguration servicesConfiguration)
        {
            bool? response = null;
            //check external stuff
            return response ?? false;
        }
    }
    

    我用这个属性修饰了我的方法:

    [IsAuthorized(SecurityAction.Demand, EntityObject = Fields.UserManagement, Field = Fields.AllowDisplay, Expected = '1')]
    public List<Group> GetUserGroups()
    {
        var response = new List<Group>();
    
        //Get the groups from the database
        var groups = groupManager.FindAll();
    
        //Map them to the output group type
        response = groups.Select(x => new Group()
        {
            ID = x.ID,
            Name = x.Name,
            Alias = x.Alias,
            Description = x.Description
        }).ToList();
    
        return response;
    }
    

    我现在想对这个方法进行单元测试,但是属性被激发了。我试过一些东西来嘲弄这个属性,但是没有成功。

    我用的是最小起订量和 Smocks .

    这是我的单元测试,没有属性的模拟实例:

    [TestMethod]
    public void GetUserGroups_UserGroupsFound_UserGroupsReturned()
    {
        Smock.Run(context =>
        {
            //Arrange
            Setup();
    
            m_Container
                        .RegisterMock<IGroupManager>()
                        .Setup(x => x.FindAllFromCache())
                        .Returns(new List<Concept.Security.MasterData.Domain.Group>()
                        {
                            new Concept.Security.MasterData.Domain.Group()
                            {
                                Name = "MyUserGroup",
                                Alias = "My User Group",
                                Description = "My user group description",
                                System = false,
                                Authorizations = "000001111100000000"
                            },
                            new Concept.Security.MasterData.Domain.Group()
                            {
                                Name = "MySecondUserGroup",
                                Alias = "My Second User Group",
                                Description = "My second user group description",
                                System = false,
                                Authorizations = "000000000000000000"
                            }
                        });
    
            var identityService = new UserManagementService(m_Container, m_UserAuthorizationManager.Object, m_IdentityService.Object);
    
            //** begin add mocked attribute **//
            //** end add mocked attribute **//
    
            //Act
            var response = identityService.GetUserGroups();
    
            //Assert
            Assert.AreEqual(2, response.Count);
            Assert.AreEqual(1, response.Where(x => x.Alias == "MyUserGroup").Count());
            Assert.AreEqual(1, response.Where(x => x.Alias == "MySecondUserGroup").Count());
            Assert.AreEqual(2, response.Where(x => x.Authorizations == null).Count());
        });
    }
    

    运行此操作会导致异常,因为该属性尝试连接外部服务,但它们没有(也无法)设置为接收请求。

    //** begin add mocked attribute **//
    var identityService = new UserManagementService(m_Container, m_UserAuthorizationManager.Object, m_IdentityService.Object);
    
    var IsAuthorizedAttribute = new Mock<IsAuthorizedAttribute>(MockBehavior.Strict, new object[] { SecurityAction.Demand });
    IsAuthorizedAttribute.Setup(x => x.CreatePermission()).Returns(new PrincipalPermission(PermissionState.None));
    TypeDescriptor.AddAttributes(identityService, IsAuthorizedAttribute.Object);
    //** end add mocked attribute **//
    

    但是这个调用的是我设置外部源的属性的构造函数。当我将此构造函数放入try/catch并静默地处理异常时,在 IsAuthorizedAttribute.Object对象

    还有哪些不触发属性的选项?

    1 回复  |  直到 7 年前
        1
  •  1
  •   cactuaroid    7 年前

    构造函数不应该访问外部;否则就很难绕过测试,你知道的。

    bool 要绕过的字段。这看起来不太好,但也许足够了。

    public class IsAuthorizedAttribute : CodeAccessSecurityAttribute
    {
        // set true in the test initialization
        private static bool s_byPass;
    
        public IsAuthorizedAttribute(SecurityAction action) : base(action)
        {
            if (!s_byPass)
            {
               // setup
            }
        }
    
        private static bool IsAuthorised(string entityObject, string field, char expected, ServicesConfiguration servicesConfiguration)
        {
            if (s_byPass) { return true; }
    
            //check external stuff
        }
    }
    

    public class IsAuthorizedAttribute : CodeAccessSecurityAttribute
    {
        // set mock here in the test initialization.
        // I assume external accessor can be a static field.
        private static ExternalAccessor m_accessor = new ExternalAccessor();
    
        private static bool IsAuthorised(string entityObject, string field, char expected, ServicesConfiguration servicesConfiguration)
        {
            return m_accessor.Check();
        }
    }
    
    public class ExternalAccessor
    {
        private bool m_initialized;
    
        private void Setup()
        {
            // setup
            m_initialized = true;
        }
    
        public virtual bool Check()
        {
            // You can call setup anytime but the constructor.
            if (!m_initialized) { Setup(); }
    
            // check external stuff
        }
    }