是否需为被调用服务抛出异常的场景编写单元测试?
是否应为被调用服务抛出异常的场景编写单元测试?
我们在评审同事的PR时,就一个问题产生了分歧:要不要为被调用服务抛出异常进而导致当前服务抛出异常的场景编写单元测试。前提是,被调用服务本身已经覆盖了完整的单元测试。
具体场景
我们有一个OrderService,职责是仅返回未过期的订单。它的GetOrderById方法会先从仓储获取订单,再调用ValidationService的ValidateOrder方法校验订单的过期时间。目前我们已经编写了正常流程的单元测试,且认可这类测试的必要性,但不确定是否要覆盖ValidationService抛出异常、最终导致OrderService也抛出异常的场景。
支持编写该测试的理由
- 能防范意外的逻辑变更:如果有人给
OrderService的方法添加try-catch块并修改逻辑(比如捕获异常后返回null),这个测试会直接失败,因为预期是抛出异常而非返回null。 - 覆盖未触及的执行路径:当前的测试没有覆盖异常抛出的分支,代码覆盖率会有缺口。
反对编写该测试的理由
- 已有全局异常处理机制:我们系统里有异常处理中间件,会统一处理这类抛出的异常。
- 过度防御导致测试冗余:如果为这个场景写测试,那所有调用
OrderService的其他服务是不是也得跟进同类测试?会造成测试量不必要的膨胀。
现有代码库的惯例
查看当前维护的代码库,类似场景下,只有当当前服务需要对异常做自定义处理(比如捕获后打日志、重新抛出其他类型异常)时,才会编写对应的单元测试。另外补充:ValidationService本身已经覆盖了正常校验和抛出异常的单元测试。
相关代码实现
校验服务接口与实现
public interface IValidationService { void ValidateOrder(DateTime expirationDate); } public class ValidationService : IValidationService { public void ValidateOrder(DateTime expirationDate) { if (expirationDate < DateTime.Now) throw new InvalidOperationException("Order expired."); } }
订单与订单服务实现
public class Order { public int Id { get; set; } public DateTime ExpirationDate { get; set; } } public interface IOrderRepository { Order Get(int id); } public class OrderService { private readonly IValidationService _validationService; private readonly IOrderRepository _orderRepository; public OrderService(IValidationService validationService, IOrderRepository orderRepository) { _validationService = validationService; _orderRepository = orderRepository; } public Order GetOrderById(int orderId) { var order = _orderRepository.Get(orderId); _validationService.ValidateOrder(order.ExpirationDate); return order; } }
测试代码
已编写的正常流程测试
[Test] public void GetOrderById_ReturnsOrder() { int orderId = 123; _orderRepositoryMock.Setup(x => x.Get(orderId)).Returns(new Order { Id = orderId, ExpirationDate = DateTime.Now.AddHours(-1) }); var order = _orderService.GetOrderById(orderId); _validationServiceMock.Verify(x=> x.ValidateOrder(It.IsAny<DateTime>()), Times.Once); Assert.That(order, Is.Not.Null); }
存疑的异常场景测试
[Test] public void GetOrderById_Throws_WhenValidationFails() { int orderId = 123; _orderRepositoryMock.Setup(x => x.Get(orderId)).Returns(new Order { Id = orderId, ExpirationDate = DateTime.Now.AddHours(-1) }); _validationServiceMock.Setup(x => x.ValidateOrder(It.IsAny<DateTime>())).Throws<InvalidOperationException>(); Assert.Throws<InvalidOperationException>(() => _orderService.GetOrderById(orderId)); _validationServiceMock.Verify(x=> x.ValidateOrder(It.IsAny<DateTime>()), Times.Once); }
希望能获取更多业内同行的观点和见解,欢迎分享。
内容的提问来源于stack exchange,提问作者326
相关产品推荐
相关产品推荐

