C# 在Xunit单元测试中使用AuthorizeAsync()测试用户是否具有特定权限
问题已更新,以便更好地解释我遇到的问题, 只要我有这个控制器C# 在Xunit单元测试中使用AuthorizeAsync()测试用户是否具有特定权限,c#,unit-testing,asp.net-core-mvc,xunit.net,asp.net-core-identity,C#,Unit Testing,Asp.net Core Mvc,Xunit.net,Asp.net Core Identity,问题已更新,以便更好地解释我遇到的问题, 只要我有这个控制器 [Authorize] public class IdeaManagementController : Controller { private IIdeaManagementService _ideaManagementService; private ITenantService _tenantService; private ITagService _t
[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);
}
}
我期望得到不同的结果,因为我将返回结果标记为true只是为了忽略这一行代码并继续查看结果,但当我再次调试测试方法时,编译器进入了验证消息,因为它没有感觉到我对AuthorizeAsync方法结果所做的更改。。
事先非常感谢你
==解决方案==
简介:
“我们不能用创造问题的思维水平来解决问题”——阿尔伯特·爱因斯坦。带着这句可爱的话,我想告诉大家,我花了大约一周的时间来解决这个问题,直到我觉得这个问题现在永远也解决不了。我花了几个小时进行调查,但在读了一篇文章,改变了思维方式后,30分钟内就找到了解决办法
问题一目了然:
简单地说,我试图对上面编写的操作方法进行单元测试,我遇到了一个严重的问题,我无法模拟“AuthorizeAsync”方法,因为它是一个内置的扩展方法,而且由于扩展方法本质上是静态方法,所以永远不能用传统的模拟类的方式来模拟它
解决方案详情:
为了能够模拟这个动作方法,我创建了一个包含静态委托的静态类,我将模拟这些委托,或者可以说,通过替换单元测试类中的静态委托来“包装”我的扩展方法,如下所示
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;
}
公共静态类DelegateFactory
{
公共静态函数授权异步=
(c、o、s)=>
{
返回AuthorizationServiceExtensions.authorizationAsync(null,null,“”);
};
}
公共模拟授权ServiceMockExtensionFactory()
{
var mockRepository=new Moq.mockRepository(Moq.MockBehavior.Strict);
var mockFactory=mockRepository.Create();
var ClaimsPrincipal=mockRepository.Create();
mockFactory.Setup(x=>x.AuthorizeAsync(It.IsAny(),It.IsAny(),It.IsAny()).ReturnsAsync(true);
返回工厂;
}
在我的测试方法中,我只调用了控制器构造函数实例化中的模拟对象
[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);
}
[事实]
public async void IdeaCoordinator当AuthroizedUser不是null而是ideamaManagement管理器授权返回视图()时
{
//安排
int approvedByID=data.GetTenantByID(1.TenantId);
_IdeaManagementControllerObject=新的IdeaManagementController
(IdeaManagementServiceMockFactory().Object,
TenantServiceMockFactory().Object,
TagServiceMockFactory().Object,
AuthorizationServiceMockExtensionFactory().Object,
EmployeeIdeaCategoryServiceMockFactory().Object);
//表演
IdeaManagementServiceMockFactory().Setup(m=>m.GetByIdeAcominator(approvedByID)).Returns(data.GetCordinatedEmpIdeas(approvedByID));
ViewResult _view=Wait _IdeaManagementControllerObject.IdeaCoordinator()作为ViewResult;
var模型=_view.model as List;
//断言
Assert.Equal(3,model.Count);
Assert.IsType(新视图结果().GetType(),_视图);
}
正如上面所说的,幸福的最大原因是感激。我要感谢斯蒂芬·福卡(Stephen Fuqua)的精彩解决方案和文章
谢谢大家:) 模拟测试所需的依赖项。测试中的方法使用了
IAuthorizationService
、iidemanagementservice
和ITenantService
。此特定测试不需要任何其他符号
将您的代码耦合到您不拥有和控制的第三方代码会使测试变得困难。我的建议是,在界面后面抽象出你可以控制的东西,这样你就有了灵活性。因此,将IAuthorizationService
更改为您自己的抽象
public interface ICustomAuthorizationService {
Task<bool> AuthorizeAsync(ClaimsPrincipal user, string policyName);
}
这是扩展方法的一个缩回示例,因为它们是静态的,如果隐藏依赖项,则很难进行测试。我在开发ASP.NET核心API时也遇到了这个问题。我的情况不那么复杂,所以不确定同样的解决方案是否适用于您 这是我的解决办法
IAuthorizationService
有两个不是扩展的方法。可以假设(我已经验证)扩展只是助手,并且将调用这些方法中的任何一种
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
要添加到markbeij的答案中,还必须实例化ClaimsPrincipal用户(可能还有其标识),否则将引发空引用异常。这是我遇到的一个问题(ASP.NET Core 5),下面是我对其他遇到此问题的人的解决方案:
// Arrange
var mockAuthorizationService = new Mock<IAuthorizationService>();
mockAuthorizationService
.Setup(a => a.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), It.IsAny<object>(), It.IsAny<string>()))
.ReturnsAsync(AuthorizationResult.Success)
.Verifiable();
// Instantiate User
var httpContext = new DefaultHttpContext();
httpContext.User = new ClaimsPrincipal();
// Add identity if you need to access the User.Identity
httpContext.User.AddIdentity(new ClaimsIdentity());
controller.ControllerContext = new ControllerContext
{
HttpContext = httpContext
};
var controller = new AccountController(mockAuthorizationService.Object);
// Act
var result = await controller.Index();
// Assert
mockAuthorizationService.Verify(a => a.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), It.IsAny<object>(), It.IsAny<string>()), Times.Once);
//排列
var mockAuthorizationService=new Mock();
模拟授权服务
.Setup(a=>a.AuthorizeAsync(It.IsAny(),It.IsAny(),It.IsAny())
.ReturnsAsync(AuthorizationResult.Success)
.可验证();
//实例化用户
var httpContext=new DefaultHttpContext();
httpContext.User=newclaimsprincipal();
//如果需要访问用户,请添加标识。标识
httpContext.User.AddIdentity(new-ClaimsIdentity());
controller.ControllerContext=新的ControllerContext
{
HttpContext=HttpContext
};
var控制器=新的AccountController(mockAuthorizationService.Object);
//表演
var result=await controller.Index();
//断言
验证(a=>a.authorizationAsync(It.IsAny(),It.IsAny(),It.IsAny()),次.Once);
但您的测试需要挑战结果。根据测试方法,在未经授权使用时发生。不清楚您在问什么。显示控制器结构
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);
}
}
services.AddSingleton<ICustomAuthorizationService, CustomAuthorizationService>();
[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);
}
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName);
var authorizeService = new Mock<IAuthorizationService>();
authorizeService.Setup(service => service.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), It.IsAny<object>(), It.IsAny<string>())).ReturnsAsync(AuthorizationResult.Success);
// Arrange
var mockAuthorizationService = new Mock<IAuthorizationService>();
mockAuthorizationService
.Setup(a => a.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), It.IsAny<object>(), It.IsAny<string>()))
.ReturnsAsync(AuthorizationResult.Success)
.Verifiable();
// Instantiate User
var httpContext = new DefaultHttpContext();
httpContext.User = new ClaimsPrincipal();
// Add identity if you need to access the User.Identity
httpContext.User.AddIdentity(new ClaimsIdentity());
controller.ControllerContext = new ControllerContext
{
HttpContext = httpContext
};
var controller = new AccountController(mockAuthorizationService.Object);
// Act
var result = await controller.Index();
// Assert
mockAuthorizationService.Verify(a => a.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), It.IsAny<object>(), It.IsAny<string>()), Times.Once);