如何通过InlineAutoMoqData在Throws/ThrowsAsync中模拟多异常类型?
动态创建指定类型异常实例的优雅方案
方法1:反射直接创建(最直接)
利用Activator.CreateInstance动态生成对应异常对象,大部分系统异常都支持无参构造,直接用即可:
[Theory] [InlineAutoMockData(typeof(HttpRequestException))] [InlineAutoMockData(typeof(InvalidOperationException))] public async Task MyTestFunction_Should_HandleExceptions(Type exceptionType, Mock<HttpClient> clientMock) { // Arrange var cut = CreateCut(); // 动态创建指定类型异常 var exception = (Exception)Activator.CreateInstance(exceptionType); clientMock.Setup(c => c.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())) .ThrowsAsync(exception); // Act var result = await cut.DoSomething(); // Assert result.Should().BeTrue(); }
如果需要带错误消息的异常,用重载传参即可:
var exception = (Exception)Activator.CreateInstance(exceptionType, "测试异常消息");
方法2:封装扩展方法提升可读性
把创建逻辑封装成扩展方法,让测试代码更简洁:
public static class TypeExtensions { public static Exception CreateException(this Type exceptionType, params object[] args) { if (!typeof(Exception).IsAssignableFrom(exceptionType)) throw new ArgumentException("传入类型必须是Exception派生类", nameof(exceptionType)); return (Exception)Activator.CreateInstance(exceptionType, args); } }
测试中直接调用:
var exception = exceptionType.CreateException(); // 或带参数 var exception = exceptionType.CreateException("自定义错误信息");
方法3:结合AutoFixture创建(贴合AutoMoq生态)
如果项目用AutoFixture,可以直接用它的Fixture实例来创建异常,自动处理构造函数参数:
[Theory] [InlineAutoMockData(typeof(HttpRequestException))] [InlineAutoMockData(typeof(InvalidOperationException))] public async Task MyTestFunction_Should_HandleExceptions(Type exceptionType, Mock<HttpClient> clientMock, IFixture fixture) { // Arrange var cut = CreateCut(); var exception = fixture.Create(exceptionType) as Exception; clientMock.Setup(c => c.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())) .ThrowsAsync(exception); // Act var result = await cut.DoSomething(); // Assert result.Should().BeTrue(); }
AutoFixture会自动匹配合适的构造函数,比手动用Activator更灵活。
方法4:泛型测试(类型安全最优解)
如果不想传递Type参数,用泛型测试完全避免类型转换,类型安全性拉满:
private async Task MyTestFunction_Should_HandleExceptions<TException>() where TException : Exception, new() { // Arrange var fixture = new Fixture().Customize(new AutoMoqCustomization()); var clientMock = fixture.Freeze<Mock<HttpClient>>(); var cut = fixture.Create<YourSutType>(); // 替代自定义的CreateCut(),用AutoFixture创建被测对象 clientMock.Setup(c => c.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())) .ThrowsAsync(new TException()); // Act var result = await cut.DoSomething(); // Assert result.Should().BeTrue(); } // 单独编写测试用例 [Fact] public async Task HandleHttpRequestException() => await MyTestFunction_Should_HandleExceptions<HttpRequestException>(); [Fact] public async Task HandleInvalidOperationException() => await MyTestFunction_Should_HandleExceptions<InvalidOperationException>();
这种方式没有类型转换风险,代码复用性高,唯一缺点是每个异常类型需要单独写一个Fact方法。
内容的提问来源于stack exchange,提问作者xMutzelx
相关产品推荐
相关产品推荐

