You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 08:02:17