如何单元测试finally块中Response.OnCompleted()内的代码?
问题:如何手动触发Response.OnCompleted委托进行单元测试
被测代码
public async Task<IActionResult> Get([FromBody] object request) { try { return Ok("Okay Message"); } catch (Exception ex) { return StatusCode(StatusCodes.Status500InternalServerError, FormProblemDetails(ex, StatusCodes.Status500InternalServerError)); } finally { Response.OnCompleted(async () => { if (Response.StatusCode == StatusCodes.Status200OK) { // unit test this method. await DoSomeWork(); } }); } } private async Task DoSomeWork() { // unit test the code present here. }
相关单元测试代码
//Arrange; var httpContext = new DefaultHttpContext(); httpContext.Request.Headers["Authorization"] = "somedummytoken"; httpContext.Response.OnCompleted(null); MyController controller = new MyController() { ControllerContext = new ControllerContext() { HttpContext = httpContext } };
用户问题
我想要对Response.OnCompleted委托内的代码进行单元测试,但目前该回调在单元测试中不会被触发,请问如何手动触发Response.OnCompleted委托?
解决方案
单元测试场景下DefaultHttpResponse的OnCompleted回调不会自动执行,可通过反射获取内部维护的回调列表并手动触发:
清理无效代码:先删掉测试代码里的
httpContext.Response.OnCompleted(null),这行添加的空回调会干扰后续测试。调用控制器方法后执行回调:
// Arrange var httpContext = new DefaultHttpContext(); httpContext.Request.Headers["Authorization"] = "somedummytoken"; var controller = new MyController() { ControllerContext = new ControllerContext() { HttpContext = httpContext } }; var request = new object(); // 替换为实际的请求对象 // Act var result = await controller.Get(request); // 手动触发OnCompleted回调 var response = httpContext.Response as DefaultHttpResponse; var callbacksField = typeof(DefaultHttpResponse).GetField( "_onCompletedCallbacks", BindingFlags.NonPublic | BindingFlags.Instance ); var callbacks = callbacksField.GetValue(response) as List<Func<Task>>; if (callbacks != null) { foreach (var callback in callbacks) { await callback(); } } // Assert // 验证DoSomeWork的执行情况,比如通过依赖注入的Mock对象验证方法调用
优化建议
如果DoSomeWork依赖外部服务,建议将该服务抽象为接口并注入控制器,用Moq等框架创建Mock对象,执行回调后直接验证Mock方法的调用情况,避免依赖反射,让测试更健壮。
内容的提问来源于stack exchange,提问作者Dutta
相关产品推荐
相关产品推荐

