Ложная пользовательская реализация CodeAccessSecurityAttribute - PullRequest
0 голосов
/ 23 октября 2018

У меня есть пользовательская реализация 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;
}

Теперь я хочу провести модульное тестирование этого метода, но атрибут срабатывает.Я пробовал некоторые вещи, чтобы смоделировать атрибут, но безуспешно.

Я использую Moq и 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 Ответ

0 голосов
/ 30 октября 2018

Конструкторы не должны иметь доступа к внешним объектам;иначе вам будет сложно обойти тестирование, как вы знаете.

Простой способ - сделать статическое 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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...