如何对含私有方法的扩展方法中InvalidModelStateResponseFactory做单元测试?
解决方案
1. 确认配置已正确注册并获取实例
你的问题核心是没拿到被正确配置后的ApiBehaviorOptions,直接模拟或手动创建的实例不会包含你在扩展方法里设置的工厂逻辑。正确做法是通过服务容器构建并获取配置后的实例:
// 初始化服务集合与MvcBuilder var services = new ServiceCollection(); var mvcBuilder = services.AddControllers(); // 调用你的自定义扩展方法 mvcBuilder.Customize(); // 构建服务提供者 var serviceProvider = services.BuildServiceProvider(); // 取出配置好的ApiBehaviorOptions var options = serviceProvider.GetRequiredService<IOptions<ApiBehaviorOptions>>().Value;
2. 手动触发InvalidModelStateResponseFactory
拿到配置后的options后,直接调用工厂方法,传入一个ModelState无效的ActionContext,就能进入你要测试的代码块:
// 构造包含错误的ModelState var modelState = new ModelStateDictionary(); modelState.AddModelError("Username", "用户名不能为空"); // 构造ActionContext(仅需必要参数,无需完整HTTP上下文) var actionContext = new ActionContext( new DefaultHttpContext(), new RouteData(), new ActionDescriptor(), modelState ); // 执行工厂方法 var responseResult = options.InvalidModelStateResponseFactory(actionContext); // 断言结果是否符合预期 Assert.IsType<BadRequestResult>(responseResult);
3. 测试私有方法的两种方式
如果需要验证工厂内部调用的私有方法逻辑,可选择以下方案:
- 转为内部方法(推荐):将私有方法改为
internal访问级别,然后在原项目的AssemblyInfo.cs中添加[assembly: InternalsVisibleTo("你的测试项目名称")],测试项目即可直接调用该方法单独验证逻辑。 - 反射调用:若不想修改原代码,用反射获取私有方法并调用:
// 假设私有方法所在类为YourExtensionClass,方法名为ProcessInvalidModelState,参数为ModelStateDictionary var type = typeof(YourExtensionClass); var privateMethod = type.GetMethod( "ProcessInvalidModelState", BindingFlags.NonPublic | BindingFlags.Static ); var methodResult = privateMethod.Invoke(null, new object[] { modelState }); // 对methodResult做断言验证
4. 常见误区排查
- 不要直接模拟
ApiBehaviorOptions:你的扩展方法是在配置该选项,必须通过服务容器构建后获取实际生效的配置实例,手动创建或模拟的实例不会包含你的自定义逻辑。 - 必须先调用
AddControllers():IMvcBuilder由AddControllers()生成,缺少这一步会导致扩展方法的配置逻辑无法正确挂载到服务体系中。
内容的提问来源于stack exchange,提问作者San
相关产品推荐
相关产品推荐

