FluentAssertions 异常断言技术详解

FluentAssertions 异常断言技术详解

引言

在单元测试中,异常处理是一个非常重要的环节。FluentAssertions 提供了一套优雅且强大的异常断言机制,让开发者能够以流畅的方式验证代码是否按预期抛出异常。本文将全面介绍 FluentAssertions 中的异常断言功能。

基本异常断言

验证方法抛出特定异常

最基本的异常断言是验证某个方法是否抛出了特定类型的异常:

subject.Invoking(y => y.Foo("Hello"))
    .Should().Throw<InvalidOperationException>()
    .WithMessage("Hello is not allowed at this moment");

这段代码验证了 Foo 方法在被调用时会抛出 InvalidOperationException,并且异常消息包含特定内容。

使用 Arrange-Act-Assert 模式

对于偏好 AAA 模式的开发者,可以使用以下方式:

Action act = () => subject.Foo2("Hello");

act.Should().Throw<InvalidOperationException>()
    .WithInnerException<ArgumentException>()
    .WithMessage("whatever");

这种方式不仅验证了异常类型,还验证了内部异常和消息内容。

高级异常验证

验证异常属性

FluentAssertions 允许对异常实例的属性进行详细验证:

var act = () => subject.Foo(null);

act.Should().Throw<ArgumentNullException>()
    .WithParameterName("message");

使用 Where 进行自定义验证

对于更复杂的验证场景,可以使用 Where 方法:

var act = () => subject.Foo(null);
act.Should().Throw<ArgumentNullException>()
   .Where(e => e.Message.StartsWith("did"));

通配符匹配异常消息

FluentAssertions 支持使用通配符匹配异常消息:

var act = () => subject.Foo(null);
act.Should().Throw<ArgumentNullException>()
   .WithMessage("?did*");

支持的通配符包括:

  • *:匹配零个或多个字符
  • ?:匹配一个字符

验证不抛出异常

基本不抛出异常验证

var act = () => subject.Foo("Hello");
act.Should().NotThrow();

验证不抛出特定异常

var act = () => subject.Foo("Hello");
act.Should().NotThrow<InvalidOperationException>();

延迟验证

对于需要等待一段时间才能确定结果的场景:

var act = () => service.IsReady().Should().BeTrue();
act.Should().NotThrowAfter(10.Seconds(), 100.Milliseconds());

第二个参数指定了重试间隔时间。

处理 yield 返回的方法

对于使用 yield 返回集合的方法,需要使用 Enumerating 强制枚举:

Func<IEnumerable<char>> func = () => obj.SomeMethodThatUsesYield("blah");
func.Enumerating().Should().Throw<ArgumentException>();

或者:

obj.Enumerating(x => x.SomeMethodThatUsesYield("blah")).Should().Throw<ArgumentException>();

精确异常匹配

使用 ThrowExactlyWithInnerExceptionExactly 可以精确匹配异常类型:

// 会失败,如果抛出的是 ApplicationException 而不是 Exception
act.Should().ThrowExactly<Exception>(); 

异步异常处理

基本异步异常验证

var act = () => asyncObject.ThrowAsync<ArgumentException>();
await act.Should().ThrowAsync<InvalidOperationException>();
await act.Should().NotThrowAsync();

使用 Awaiting 方法

var act = asyncObject.Awaiting(x => x.ThrowAsync<ArgumentException>());
await act.Should().ThrowAsync<ArgumentException>();

异步延迟验证

await act.Should().NotThrowAfterAsync(2.Seconds(), 100.Milliseconds());

简化语法

使用 FluentActions 可以进一步简化代码:

FluentActions.Invoking(() => MyClass.Create(null))
    .Should().Throw<ArgumentNullException>();

或者使用静态导入:

using static FluentAssertions.FluentActions;

Invoking(() => MyClass.Create(null))
    .Should().Throw<ArgumentNullException>();

处理 AggregateException

FluentAssertions 会自动解包 AggregateException,使得断言可以正常工作:

// 即使异常被包装在 AggregateException 中,也能正常工作
act.Should().Throw<InvalidOperationException>(); 

ThrowExactly 不会自动解包,它要求异常必须精确匹配。

总结

FluentAssertions 提供了全面而强大的异常断言功能,无论是同步还是异步代码,都能以流畅的方式进行验证。通过本文介绍的各种方法,开发者可以编写出更清晰、更健壮的单元测试代码。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余媛奕Lowell

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值