The question has been Updated for better explanation to the issue I have,
Simply I have this controller,
[Authorize]
publi
I encountered this issue as well while developing an ASP.NET Core API. My situation was less complex, so not sure if the same solutions is applicable for you.
Here is my solution
IAuthorizationService
has two methods that are not extensions. One can assume (and I verified) that the extensions are just helpers and will call either one of these methods.
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
So mocking IAuthorizationService
for me was as simple as doing the following:
var authorizeService = new Mock<IAuthorizationService>();
authorizeService.Setup(service => service.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), It.IsAny<object>(), It.IsAny<string>())).ReturnsAsync(AuthorizationResult.Success);
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.
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>();
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.