.NET Core预约系统XUnit单元测试无法捕获自定义HttpResponseException
问题描述
我基于.NET Core开发了一款预约系统,为实现自定义错误格式,移除了默认API控制器中间件,改为抛出自定义HttpResponseException异常,再通过IActionFilter统一处理异常。但编写该异常抛出逻辑的XUnit单元测试时遇到问题:测试会在抛出异常处直接终止,无法完成断言。我已经尝试过在测试中使用try-catch捕获、Assert.Throws断言,甚至关闭调试器运行测试,都没能解决问题。
业务层代码(CreateAppointment方法)
public async Task<Appointment> CreateAppointment(AppointmentCreateDto dto) { var existingAppointment = await _appointmentRepository.GetByTimeSlot(dto.StartTime, dto.EndTime); if (existingAppointment != null) { throw new HttpResponseException(HttpStatusCode.Conflict, "该时间段已有预约"); } // 其他预约创建逻辑 }
单元测试代码
[Fact] public async Task CreateAppointment_WhenTimeSlotExists_ThrowsHttpResponseException() { _mockRepo.Setup(r => r.GetByTimeSlot(It.IsAny<DateTime>(), It.IsAny<DateTime>())) .ReturnsAsync(new Appointment()); var service = new AppointmentService(_mockRepo.Object); var dto = new AppointmentCreateDto { StartTime = DateTime.Now, EndTime = DateTime.Now.AddHours(1) }; // 尝试用Assert.Throws但未成功捕获异常 var exception = Assert.Throws<HttpResponseException>(() => service.CreateAppointment(dto).Wait()); Assert.Equal(HttpStatusCode.Conflict, exception.StatusCode); }
解决方案
1. 针对异步方法使用Assert.ThrowsAsync
你的CreateAppointment是异步方法,直接用Assert.Throws配合.Wait()会导致异常被封装进AggregateException,无法直接捕获目标异常。改用XUnit专为异步方法设计的Assert.ThrowsAsync即可:
[Fact] public async Task CreateAppointment_WhenTimeSlotExists_ThrowsHttpResponseException() { _mockRepo.Setup(r => r.GetByTimeSlot(It.IsAny<DateTime>(), It.IsAny<DateTime>())) .ReturnsAsync(new Appointment()); var service = new AppointmentService(_mockRepo.Object); var dto = new AppointmentCreateDto { StartTime = DateTime.Now, EndTime = DateTime.Now.AddHours(1) }; // 用ThrowsAsync捕获异步抛出的异常 var exception = await Assert.ThrowsAsync<HttpResponseException>(() => service.CreateAppointment(dto)); // 断言异常状态码和消息 Assert.Equal(HttpStatusCode.Conflict, exception.StatusCode); Assert.Equal("该时间段已有预约", exception.Message); }
2. 确认HttpResponseException的定义合法性
检查自定义异常类是否正确继承自标准Exception,避免因类型定义问题导致无法捕获:
public class HttpResponseException : Exception { public HttpStatusCode StatusCode { get; } public HttpResponseException(HttpStatusCode statusCode, string message) : base(message) { StatusCode = statusCode; } }
3. 排除测试项目引用问题
确保测试项目能正确引用到HttpResponseException所在的程序集,避免因跨程序集类型识别失败导致捕获异常失败。
4. 隔离API管道逻辑
单元测试直接调用业务层方法时,IActionFilter属于API请求管道组件,不会影响业务层的异常抛出逻辑,无需在测试中配置相关中间件。
内容的提问来源于stack exchange,提问作者vishwa Tj
相关产品推荐
相关产品推荐

