如何自定义FluentAssertion的错误提示信息以显示具体泛型类型名称?
The Problem
I'm working with FluentAssertions to write integration tests for service registration, and I need to tweak the error messages to be more descriptive. Here's my current test code:
using (new AssertionScope()) { foreach (var parameterType in parameterTypes) { var fooType = typeof(IFoo<>); var genericType = fooType.MakeGenericType(parameterType); serviceProvider .GetService(genericType) .Should() .NotBeNull($"all Foo types should be registered"); } }
When a test fails, the default error message looks like this:
Expected serviceProvider.GetService(genericProviderType) not to be
because all Foo types should be registered.
But I want it to display the human-friendly name of the generic type instead, using my existing type.GetPrettyName() method:
Expected IFoo
not to be because all Foo types should be registered.
I've tried digging through FluentAssertions docs and source code, thinking I needed to modify the subject or identifier, but couldn't find the right approach. I also messed around with deferred context functions in the AssertionScope constructor, but ran into closure issues where the value got overwritten in the loop.
The Solution
The fix is straightforward: use FluentAssertions' WithIdentifier() method to explicitly set the subject text that appears in your error message. This replaces the auto-generated expression string with your custom pretty-printed type name.
Here's the updated code:
using (new AssertionScope()) { foreach (var parameterType in parameterTypes) { var fooType = typeof(IFoo<>); var genericType = fooType.MakeGenericType(parameterType); var prettyTypeName = genericType.GetPrettyName(); // Your existing formatting method serviceProvider .GetService(genericType) .Should() .WithIdentifier(prettyTypeName) // Override the default subject here .NotBeNull($"all Foo types should be registered"); } }
How This Works
WithIdentifier()lets you replace the default subject (the expression that FluentAssertions parses, likeserviceProvider.GetService(genericType)) with any string you want. In this case, we use yourGetPrettyName()output to show the clean generic type.- Unlike the deferred context approach you tried earlier, this sets the identifier per-assertion inside the loop, so each iteration uses the correct
genericTypevalue without closure-related bugs.
Bonus: More Readable Reason Syntax
If you prefer a cleaner chain, you can use the Because() method instead of passing the reason directly to NotBeNull()—this works seamlessly with WithIdentifier():
serviceProvider .GetService(genericType) .Should() .WithIdentifier(prettyTypeName) .NotBeNull() .Because("all Foo types should be registered");
内容的提问来源于stack exchange,提问作者ilitirit




