使用Fluent Validation和Xunit编写单元测试时,如何正确抛出ValidationException?
问题分析与修复方案
你的测试报错显示期望捕获AggregateException但实际无异常抛出,核心问题出在异步方法的异常处理方式和Mock匹配逻辑上,以下是具体修复步骤:
1. 改用异步断言方法
ProcessOrderAsync是异步方法,用.Wait()同步等待会导致异常被异常包装或吞掉,Xunit提供了专门的异步断言方法Assert.ThrowsAsync,应直接替代同步的Assert.Throws。
2. 修正Mock的匹配逻辑
你当前的Mock设置是_mock.Setup(x => x.Validate(order)).Throws(...),这是引用匹配——只有当ProcessOrderAsync内部传入的Order实例和你setup中用的order完全是同一个对象时,才会触发抛出异常。如果ProcessOrderAsync内部对Order做了复制、转换,这个Mock就不会生效。建议改用类型匹配,确保任意Order实例调用Validate都触发异常(或根据验证规则调整匹配条件)。
修复后的完整测试代码
[Fact] [Trait("Category", "Unit")] public async Task Should_MyOrder_Throw_ValidationException_Successfully() { // Arrange var orderId = "123"; var fixture = new Fixture(); fixture.Customize<Order>(composer => composer.With(_ => _.OrderNo, orderId) .With(_ => _.DeliveryNo, 0) .With(_ => _.Name, "XXXXXX")); var order = fixture.Create<Order>(); var orderEvent = new OrderEvent( CloudEventFactory.CreateNew(OrderEvents.OrderUpsert, orderId, order)); // 改用It.IsAny<Order>()确保匹配任意Order实例,避免引用匹配失效 _mock.Setup(x => x.Validate(It.IsAny<Order>())) .Throws(new ValidationException("ValidationException")); _orderRepositoryMock .Setup(_ => _.FindByOrderIdAsync(orderId, OrderEvents.EventType, It.IsAny<CancellationToken>())) .ReturnsAsync((OrderEvent)null); // Act & Assert // 直接用ThrowsAsync捕获异步方法抛出的ValidationException var exception = await Assert.ThrowsAsync<ValidationException>(() => _sut.ProcessOrderAsync(orderEvent, new CancellationToken())); // 可选:验证异常消息是否符合预期 Assert.Equal("ValidationException", exception.Message); }
额外说明
- 如果
ProcessOrderAsync内部确实会把ValidationException包装成AggregateException抛出(比如用了Task.Wait()或其他同步阻塞逻辑),可改用如下断言方式:var aggregateException = await Assert.ThrowsAsync<AggregateException>(() => _sut.ProcessOrderAsync(orderEvent, new CancellationToken())); Assert.IsType<ValidationException>(aggregateException.InnerException); - 可在测试末尾加
_mock.Verify(x => x.Validate(It.IsAny<Order>()), Times.Once);,验证Validate方法确实被调用,这是排查Mock未触发的常用手段。
内容的提问来源于stack exchange,提问作者Penguen
相关产品推荐
相关产品推荐

