Lau*_*son 1 c# unit-testing asp.net-core razor-pages
我有一个 Razor Pages 应用程序,其中站点中的所有页面都使用多项服务BasePageModel,并将这些服务添加到我在每个 Razor 页面上继承的 。我的BasePageModel看起来像这样:
public abstract class BasePageModel : PageModel
{
private IUserClaimsService _userClaims;
private IAuthorizationService _authService;
protected virtual IUserClaimsService UserClaimsService => _userClaims ?? (_userClaims = HttpContext.RequestServices.GetService<IUserClaimsService>());
protected virtual IAuthorizationService AuthService => _authService ?? (_authService = HttpContext.RequestServices.GetService<IAuthorizationService>());
}
Run Code Online (Sandbox Code Playgroud)
Razor 页面本身看起来像这样:
public class IndexModel : BasePageModel
{
public async Task<ActionResult> OnGet()
{
var HasAccess = (await AuthService.AuthorizeAsync(User, "PermissionName")).Succeeded;
if (!HasAccess)
{
return new ForbidResult();
}
return Page();
}
}
Run Code Online (Sandbox Code Playgroud)
我正在创建单元测试来测试 Razor 页面上的授权。我的授权具有取决于用户声明的策略。我想做的是模拟用户声明并相应地测试授权是否成功或失败,具体取决于页面。但是,我无法UserClaimsService从我的测试中模拟它。我实例化了 pageModel 但无法获取任何值BasePageModel(例如UserClaimsService)。知道我该怎么做吗?
应避免通过请求服务使用服务定位器反模式。通过构造函数注入遵循显式依赖原则。
public abstract class BasePageModel : PageModel {
protected readonly IAuthorizationService authService;
protected BasePageModel(IAuthorizationService authService) {
this.authService = authService;
}
protected async Task<bool> HasAccess(string permissionName) {
return (await authService.AuthorizeAsync(User, permissionName)).Succeeded;
}
}
Run Code Online (Sandbox Code Playgroud)
Razor 页面本身将变成
public class IndexModel : BasePageModel {
public IndexModel(IAuthorizationService authService) : base (authService) {
//...
}
public async Task<ActionResult> OnGet() {
if (!HasAccess("PermissionName")) {
return new ForbidResult();
}
return Page();
}
}
Run Code Online (Sandbox Code Playgroud)
对于现在的测试,可以根据隔离单元测试的需要模拟显式依赖关系
public async Task Index_Should_Be_Forbidden() {
// Arrange
var authService = Mock.Of<IAuthorizationService>(_ =>
_.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), null, It.IsAny<string>()) ==
Task.FromResult(AuthorizationResult.Failed())
);
//Create test user with what ever claims you want
var displayName = "UnAuthorized User name";
var identity = new GenericIdentity(displayName);
var principle = new ClaimsPrincipal(identity);
// use default context with user
var httpContext = new DefaultHttpContext() {
User = principle
}
//need these as well for the page context
var modelState = new ModelStateDictionary();
var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
var modelMetadataProvider = new EmptyModelMetadataProvider();
var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
// need page context for the page model
var pageContext = new PageContext(actionContext) {
ViewData = viewData
};
//create model with necessary dependencies
var model = new IndexModel(authService) {
PageContext = pageContext //context includes User
};
//Act
var result = await model.OnGet();
//Assert
result.Should()
.NotBeNull()
.And.BeOfType<ForbidResult>();
}
Run Code Online (Sandbox Code Playgroud)
ASP.NET Core 中的参考Razor Pages 单元测试
您似乎还混合了涉及用户声明和授权的跨领域关注点,但我想这超出了当前要求的范围。
参考ASP.NET Core 中的 Razor Pages 授权约定