如何在AuthorizationHandler单元测试中模拟RouteData?
ASP.NET Core 授权处理程序单元测试中读取路由数据的问题
我在Web API项目里有一个授权处理程序(AuthorizationHandler),运行时能正常读取路由数据(非查询字符串),但单元测试时怎么都搞不定,试了好几种HttpContext模拟方式都达不到预期效果。
补充说明:测试时希望httpContext.GetRouteValue("param")返回"myparamvalue",就像实际调用localhost:8080/api/controller/myparamvalue/my-action端点时的结果一样。
API端点代码
[Authorize(Policy = "MyCustomPolicy")] [Route("{param}/my-action")] [HttpGet] public ActionResult<string> GetResult([FromRoute] string param) { return Content("Dummy response"); }
授权处理程序代码
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement) { // 其他代码 string paramValue = string.Empty; if (context.Resource is HttpContext httpContext) { paramValue = Convert.ToString(httpContext.GetRouteValue("param")); } // 使用paramValue的后续代码 }
当前单元测试代码
public async Task SucceedWhenCalledWithParam() { //Arrange ClaimsPrincipal principal = CreatePrincipal("testscope", "scope"); var requirement = new MyRequirement(); var httpContext = new DefaultHttpContext(); httpContext.Request.Host = new HostString("localhost:8080"); httpContext.Request.Path = new PathString("/api/controller/myparamvalue/my-action"); AuthorizationHandlerContext context = new(new[] { requirement }, principal, httpContext); MyAuthHandler authHandler = new(); //Act await authHandler.HandleAsync(context).ConfigureAwait(false); //Assert Assert.IsTrue(context.HasSucceeded); }
解决方案
问题根源:直接设置Request.Path不会自动解析路由数据,GetRouteValue读取的是HttpContext.Request.RouteValues集合中的数据,这个集合需要手动填充或通过路由匹配器生成。
方案1:手动添加路由值(简单直接)
修改单元测试的Arrange部分,直接给RouteValues添加目标参数:
public async Task SucceedWhenCalledWithParam() { //Arrange ClaimsPrincipal principal = CreatePrincipal("testscope", "scope"); var requirement = new MyRequirement(); var httpContext = new DefaultHttpContext(); // 手动添加路由参数值 httpContext.Request.RouteValues.Add("param", "myparamvalue"); AuthorizationHandlerContext context = new(new[] { requirement }, principal, httpContext); MyAuthHandler authHandler = new(); //Act await authHandler.HandleAsync(context).ConfigureAwait(false); //Assert Assert.IsTrue(context.HasSucceeded); }
方案2:模拟完整路由匹配(更贴近真实场景)
如果需要验证路由模板与请求路径的对应关系,可以使用路由匹配器模拟真实解析过程:
public async Task SucceedWhenCalledWithParam() { //Arrange ClaimsPrincipal principal = CreatePrincipal("testscope", "scope"); var requirement = new MyRequirement(); var httpContext = new DefaultHttpContext(); httpContext.Request.Path = new PathString("/api/controller/myparamvalue/my-action"); // 配置对应路由模板 var routeOptions = new RouteOptions(); routeOptions.Routes.Add(new Route( name: "TestRoute", template: "/api/controller/{param}/my-action", defaults: new RouteValueDictionary(), constraints: new RouteValueDictionary(), handler: null )); // 创建匹配器并执行路由匹配 var matcherFactory = new DefaultMatcherFactory(); var matcher = matcherFactory.CreateMatcher(routeOptions); var matchResult = matcher.Match(httpContext); // 将匹配得到的路由值赋值给HttpContext foreach (var kvp in matchResult.Values) { httpContext.Request.RouteValues.Add(kvp.Key, kvp.Value); } AuthorizationHandlerContext context = new(new[] { requirement }, principal, httpContext); MyAuthHandler authHandler = new(); //Act await authHandler.HandleAsync(context).ConfigureAwait(false); //Assert Assert.IsTrue(context.HasSucceeded); }
内容的提问来源于stack exchange,提问作者Vinod
相关产品推荐
相关产品推荐

