C#单元测试中Mock ASP.NET控制器抛出ServiceNotFound错误的实现方案
核心思路
你要触发ServiceNotFound类型的ServiceException,本质是让IOrdersService.PutOrdersAsync执行到抛出该异常的业务分支,根据测试目标不同,有两种实现方案:
方案1:测试控制器的异常捕获逻辑
如果你的测试目标是验证控制器能正确捕获并处理ServiceException,不需要构造真实的OrdersService实例,直接MockIOrdersService的行为即可:
// Tests.cs 示例代码(基于Moq框架) using Moq; using Microsoft.AspNetCore.Mvc; using System.Net; // 1. 构造IOrdersService模拟对象 var mockOrdersService = new Mock<IOrdersService>(); // 2. 配置PutOrdersAsync被调用时直接抛出目标NotFound异常 mockOrdersService .Setup(s => s.PutOrdersAsync()) .ThrowsAsync(new ServiceException(HttpStatusCode.NotFound, "目标服务未找到")); // 3. 将模拟服务注入控制器 var controller = new OrdersController(mockOrdersService.Object); // 4. 调用接口并断言结果符合预期 var result = await controller.PutOrdersAsync(); var objectResult = result as ObjectResult; Assert.Equal((int)HttpStatusCode.NotFound, objectResult.StatusCode);
方案2:测试服务层本身的异常抛出逻辑
如果你的测试目标是验证OrdersService本身能在资源不存在时正确抛出ServiceNotFound异常,需要配置DbContextMock返回触发异常的条件:
假设你的OrdersService.PutOrdersAsync内部逻辑是「先查询对应资源,查询不到则抛出NotFound异常」,按如下方式配置Mock即可:
// Tests.cs 示例代码(基于Moq框架) using Moq; using System.Net; using Microsoft.EntityFrameworkCore; // 1. 构造空的模拟DbSet,模拟查询不到资源的场景 var emptyOrderSet = new List<Order>().AsQueryable().BuildMockDbSet(); // 2. 配置DbContext返回空的DbSet var context = new DbContextMock(); context.Setup(c => c.Orders).Returns(emptyOrderSet.Object); // 3. 构造真实的OrdersService实例 var service = GetOrdersService(context.Object, _pdcClientFactory.Object); // 4. 调用方法断言抛出预期异常 var exception = await Assert.ThrowsAsync<ServiceException>(() => service.PutOrdersAsync()); Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode);
注意事项
- 如果使用非Moq的Mock框架(如NSubstitute),只需替换对应配置模拟行为的语法即可,核心逻辑一致。
- 请先确认你的
ServiceException构造支持传入HttpStatusCode.NotFound作为状态码参数,保证异常的StatusCode属性符合预期。
内容的提问来源于stack exchange,提问作者Mardymar
相关产品推荐
相关产品推荐

