Testing a user if they have specific authority using AuthorizeAsync () in Xunit-Unit testing

The question has been updated to better explain the problem I am having,

I just have this controller,

    [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);
    }
}

      

and I just need to check the restored viewResult of the IdeaCoordinator action method, but I couldn't just because if the validation method is _authorizationService.AuthorizeAsync , I tried to mock this method, but I just couldn't, because it is a built-in method , then I tried create a solution to work by creating a new interface that implements IAuthorizationService and a Mock that set up an interface like this

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 while I call the constructor of the controller, and then I found that like this:

[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);
}

      

I was expecting different results because I noticed that the return result is true by simply ignoring this line of code and continuing to browse, but while I debugged my test method again, the compiler ended up in the validation message as it did not feel the changes I made as a result of the AuthorizeAsync method. enter image description here

Thank you so much in advance.

== Solution ==

Introduction:

"We cannot solve our problems with the same level of thinking that created them" - Albert Einstein. and with this delightful saying I would like to report that I spent about 1 week solving this problem until I felt that at this point it would not be solved, I spent a lot of time investigating, but after reading the article and changing my mindset, the solution came in 30 minutes.

The problem at a glance:

It's just that I was trying to unit test the action method written above and I have a serious problem related to the fact that I could not mock the "AuthorizeAsync" method and simply because it is a built-in extension method and because of the extension method as a static method he can never be mocked with the traditional way of mocking classes.

Solution in detail:

To be able to mock this action method, I created a static class that contains static delegates, and with these delegates, I will mock or, as you might say, "Wrap" my extension method by replacing my static delegates in my unit test as follows.

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 in my test method I just called the mocked object in the controller constructor instance.

    [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 he said, the single main reason for happiness is gratitude. I would like to thank Stephen Fuku for his brilliant solution and article, http://www.safnet.com/writing/tech/2014/04/making-mockery-of-extension-methods.html

Thanks everyone :)!

+3


source to share


1 answer


Check the required dependencies for the test. The test method uses IAuthorizationService

, IIdeaManagementService

and ITenantService

. Nothing else is required for this particular test.

Linking code to third party code that you don't own and control makes testing difficult. My suggestion is to abstract away from what you control behind the interface so that you have this flexibility. So change IAuthorizationService

for your own abstraction.

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

      

The implementation will wrap the actual authorization service which 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);
    }
}

      

Be sure to register your packaging. eg.



services.AddSingleton<ICustomAuthorizationService, CustomAuthorizationService>();

      

If the identity has already been added to the service collection, then it IAuthorizationService

will be injected into your custom service when resolved.

So now for your test, you can poke fun at the interfaces you control and not worry about breaking third-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 extension method return paths because they are static and difficult to test if they hide dependencies.

+4


source







All Articles