使用Xunit-Unit Testing中的AuthorizeAsync()测试用户是否具有特定权限 [英] Testing user if he has a specific authority using AuthorizeAsync() in Xunit-Unit Testing

查看:96
本文介绍了使用Xunit-Unit Testing中的AuthorizeAsync()测试用户是否具有特定权限的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题已更新,以更好地解释我遇到的问题

我只有这个控制器

    [Authorize]
    public class IdeaManagementController : Controller
    {
        private IIdeaManagementService _ideaManagementService;

        private ITenantService _tenantService;

        private ITagService _tagService;

        private IEmployeeIdeaCategoryService _ideaManagementCategoryService;

        private static PbdModule _modul = PbdModule.IdeaManagement;

        IAuthorizationService _authorizationService;

        public IdeaManagementController(
            IIdeaManagementService ideaManagementService,
            ITenantService tenantService,
            ITagService tagService,
            IAuthorizationService authorizationService,
            IEmployeeIdeaCategoryService ideaManagementCategoryService)
        {
            _ideaManagementService = ideaManagementService;
            _tenantService = tenantService;
            _tagService = tagService;
            _authorizationService = authorizationService;
            _ideaManagementCategoryService = ideaManagementCategoryService;
        }

    public async Task<IActionResult> IdeaCoordinator()
    {
        if (!await _authorizationService.AuthorizeAsync(User, "IdeaManagement_Coordinator"))
        {
            return new ChallengeResult();
        }
        var ideas = _ideaManagementService.GetByIdeaCoordinator(_tenantService.GetCurrentTenantId());
        return View(ideas);
    }
}

并且我只需要测试操作方法IdeaCoordinator的retrned viewResult,但是我不能简单地因为如果_authorizationService.AuthorizeAsync验证方法,我试图模拟该方法,但是我不能因为它是扩展内置方法,然后我尝试通过创建实现IAuthorizationService的新接口和模拟该接口的自定义

and simply I need to test the retrned viewResult of the action method IdeaCoordinator but I couldn't simply because if the _authorizationService.AuthorizeAsync validation Method, I tried to mock the method but simply I couldn't because it is an Extension Built In Method , then I tried to make a work around solution by creating a new Interface that implements IAuthorizationService and Mock that customized Interface like that

public interface ICustomAuthorizationService : IAuthorizationService
{
    Task<bool> AuthorizeAsync(ClaimsPrincipal user, string policyName);
}


public IAuthorizationService CustomAuthorizationServiceFactory()
{
   Mock<ICustomAuthorizationService> _custom = new Mock<ICustomAuthorizationService>();
    _custom.Setup(c => c.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), "IdeaManagement_Coordinator")).ReturnsAsync(true);
    return _custom.Object;
}

并在我调用控制器构造函数时将其注入,然后发现自己是这样的:

and Inject it to while I am calling the controller constructor and then I found myself like that :

[Theory]
[InlineData(1)]
public async void IdeaManager_Should_Return_ViewResult(int _currentTenanatID)
{
    // Arrange ..
    _IdeaManagementControllerObject = new IdeaManagementController
                                      (IdeaManagementServiceMockFactory(_currentTenanatID),
                                      TenantServiceMockFactory(_currentTenanatID),
                                       TagServiceMockFactory(),
                                       AuthorizationServiceMockFactory(),
                                       EmployeeIdeaCategoryServiceMockFactory());
    // Act 
    var _view = await _IdeaManagementControllerObject.IdeaCoordinator() as ViewResult;

    // Assert 
    Assert.IsType(new ViewResult().GetType(), _view);
}

我期望得到不同的结果,因为我将返回结果标记为true只是为了忽略这一行代码并继续执行Viewresult,但是当我再次调试Test Method时,编译器进入了验证消息,因为它感觉不到我对AuthorizeAsync方法结果进行的更改..

I expected different results because I marked the return result to be true just to ignore this line of code and continue to the Viewresult but while I debugged my Test Method again the compiler got into the validation message as it didn't feel the changes I did on the AuthorizeAsync Method result ..

非常感谢您.

==解决方案==

简介:

我们无法用创造问题的相同思维水平来解决问题"-艾伯特·爱因斯坦.谨以这句可爱的谚语告诉我,我花了大约1周的时间解决了这个问题,直到感觉到此刻永远无法解决为止,我花了几个小时进行调查,但是在阅读了一篇文章并改变了思维方式之后,解决方案在30分钟内完成.

"We can not solve our problems with the same level of thinking that created them" - Albert Einstein. and with this adorable saying I would like to inform that I spent about 1 week for solving this problem until I felt it will never solved at this moment, I spent hours of investigation, but after reading an article and after changing the way of thinking , the solution came in 30 mins.

问题一目了然:

简而言之,我正在尝试对上面编写的action方法进行单元测试,但与此相关的一个严重问题是我无法模拟方法"AuthorizeAsync",这仅仅是因为它是内置的扩展方法,并且由于扩展方法性质作为静态方法,永远无法使用传统的模拟类方法进行模拟.

Simply, I was trying to unit test the written above action method, and I have a serious problem related with that I couldn't mock the method "AuthorizeAsync" and simply because it is a built in extension method and because of extension method nature as static method it can never be mocked with the traditional way of mocking classes.

详细信息解决方案:

为了能够模拟此操作方法,我创建了一个包含静态委托的静态类,并使用这些委托进行模拟,或者可以说,通过在单元测试中替换我的静态委托来扩展"我的扩展方法类如下.

To be able to mock this action method I created a static class that contains static delegates and with those delegates I will mock or as it can be said "Wrap" my extension method by a replacement of my static delegates in my unit test class as following.

public static class DelegateFactory
{
    public static Func<ClaimsPrincipal, object, string, Task<bool>> AuthorizeAsync =
        (c, o, s) =>
        {
            return AuthorizationServiceExtensions.AuthorizeAsync(null, null, "");
        };
}

public Mock<IAuthorizationService> AuthorizationServiceMockExtensionFactory()
{
    var mockRepository = new Moq.MockRepository(Moq.MockBehavior.Strict);
    var mockFactory = mockRepository.Create<IAuthorizationService>();
    var ClaimsPrincipal = mockRepository.Create<ClaimsPrincipal>();
    mockFactory.Setup(x => x.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), It.IsAny<object>(), It.IsAny<string>())).ReturnsAsync(true);
    return mockFactory;
}

在我的测试方法中,我只是在控制器构造函数实例化中调用了模拟对象.

and at my test method I just called the mocked object in the controller constructor instantiation.

    [Fact]
    public async void IdeaCoordinator_When_AuthroizedUser_IsNotNull_But_IdeaManagement_Manager_Authorized_Return_View()
    {
       // Arrange
        int approvedByID = data.GetTenantByID(1).TenantId;
        _IdeaManagementControllerObject = new IdeaManagementController
                                          (IdeaManagementServiceMockFactory().Object,
                                          TenantServiceMockFactory().Object,
                                           TagServiceMockFactory().Object,
                                           AuthorizationServiceMockExtensionFactory().Object,
                                           EmployeeIdeaCategoryServiceMockFactory().Object);
        //Act
        IdeaManagementServiceMockFactory().Setup(m => m.GetByIdeaCoordinator(approvedByID)).Returns(data.GetCordinatedEmpIdeas(approvedByID));
        ViewResult _view = await _IdeaManagementControllerObject.IdeaCoordinator() as ViewResult;
        var model = _view.Model as List<EmployeeIdea>;
        // Assert
        Assert.Equal(3, model.Count);
        Assert.IsType(new ViewResult().GetType(), _view);
    }

正如它所说的,最大的幸福原因就是感恩.我要感谢斯蒂芬·富夸的出色解决方案和文章,

and as it said, The single greatest cause of happiness is gratitude.I would like to thank Stephen Fuqua for his brilliant solution and article , http://www.safnet.com/writing/tech/2014/04/making-mockery-of-extension-methods.html

谢谢大家:)!

推荐答案

模拟测试所需的依赖项.被测方法使用IAuthorizationServiceIIdeaManagementServiceITenantService.此特定测试不需要其他所有内容.

Mock the necessary dependencies for the test. The method under test makes use of IAuthorizationService,IIdeaManagementService,and ITenantService. Everythign else is not needed for this particular test.

将您的代码与您不拥有和控制的第三方代码耦合在一起,使测试变得困难.我的建议是将其抽象到您控制的界面后面,以便您具有这种灵活性.因此,将IAuthorizationService更改为您自己的抽象.

Coupling your code to 3rd party code you don't own and control makes it difficult to test. My suggestion is to abstract that behind an interface you control so you have that flexibility. So change out IAuthorizationService for your own abstraction.

public interface ICustomAuthorizationService {
     Task<bool> AuthorizeAsync(ClaimsPrincipal user, string policyName);
}

该实现将包装使用扩展方法的实际授权服务

The implementation would wrap the actual authorization service that uses the extension method

public class CustomAuthorizationService: ICustomAuthorizationService {
    private readonly IAuthorizationService service;

    public CustomAuthorizationService(IAuthorizationService service) {
        this.service = service;
    }

    public Task<bool> AuthorizeAsync(ClaimsPrincipal user, string policyName) {
        return service.AuthorizeAsync(user, policyName);
    }
}

确保注册您的包装器.例如.

Make sure to register your wrapper. for example.

services.AddSingleton<ICustomAuthorizationService, CustomAuthorizationService>();

如果已将标识添加到服务集合中,则IAuthorizationService将在解决后注入到您的自定义服务中.

If Identity is already added to service collection then the IAuthorizationService will get injected into your custom service when resolved.

因此,现在进行测试时,您可以模拟自己控制的界面,而不必担心会破坏第三方代码.

So now for your test you can mock the interfaces you control and not have to worry about breaking 3rd party code.

[Theory]
[InlineData(1)]
public async void IdeaManager_Should_Return_ViewResult(int _currentTenanatID) {
    // Arrange ..
    var ideaManagementService = new Mock<IIdeaManagementService>();
    var tenantService = new Mock<ITenantService>();
    var authorizationService = new Mock<ICustomAuthorizationService>();
    var sut = new IdeaManagementController(
                     ideaManagementService.Object,
                     tenantService.Object,
                     null,
                     authorizationService.Object,
                     null);

     authorizationService
         .Setup(_ => _.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), "IdeaManagement_Coordinator"))
         .ReturnsAsync(true);

     tenantService
         .Setup(_ => _.GetCurrentTenantId())
         .Returns(_currentTenanatID);

     var ideas = new //{what ever is your expected return type here}
     ideaManagementService
         .Setup(_ => _.GetByIdeaCoordinator(_currentTenanatID))
         .Returns(ideas);

    // Act 
    var _view = await sut.IdeaCoordinator() as ViewResult;

    // Assert
    Assert.IsNotNull(_view);
    Assert.IsType(typeof(ViewResult), _view);
    Assert.AreEqual(ideas, view.Model);
}

这是扩展方法的缺点之一,因为它们是静态的并且很难测试它们是否隐藏依赖项.

This is one example of the draw backs of extension methods as they are static and difficult to test if they hide dependencies.

这篇关于使用Xunit-Unit Testing中的AuthorizeAsync()测试用户是否具有特定权限的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆