如何配置FluentAssertions使用DebuggerDisplay或ToDebugString生成精简错误消息
问题场景
我有一个包含A、B、C属性的MyData类,因其他原因无法重写ToString方法,但已通过[DebuggerDisplay("{ToDebugString()}")]配置了调试显示,且类中包含ToDebugString()方法:
[DebuggerDisplay("{ToDebugString()}")] public class MyData { public string A {get;set;} public string B {get;set;} public string C {get;set;} public string ToDebugString() => $"{A}-{B}-{C}"; }
我使用FluentAssertions对MyData对象列表做断言:
var items = new MyData[] { ... }; items.Should().Contain(i => i.A == "foo");
断言失败时,错误消息会显示完整的对象结构,内容冗长:
Expected items
{
MyProject.MyData
{
A = "a1",
B = "b1",
C = "c1"
}
} to have an item matching (i.A == "foo"). (0.2s)
虽然重写ToString可让FluentAssertions生成精简消息,但我无法修改该类。请问如何配置FluentAssertions使用ToDebugString方法,或通用使用DebuggerDisplay配置的方法来生成错误消息?
解决方案
方法1:为MyData类型注册自定义格式化器
FluentAssertions支持为特定类型注册自定义对象格式化器,直接调用目标类的ToDebugString()方法生成精简文本:
// 在测试初始化阶段注册(比如测试类的构造函数或[SetUp]方法中) FluentAssertions.Formatting.Formatter.Add(new MyDataFormatter()); public class MyDataFormatter : IValueFormatter { public bool CanHandle(Type type) { return type == typeof(MyData); } public void Format(object value, FormattedObjectGraph formattedGraph, FormattingContext context, FormatChild formatChild) { var myData = (MyData)value; formattedGraph.AddFragment(myData.ToDebugString()); } }
注册完成后,所有MyData对象在断言失败消息中都会显示ToDebugString()返回的精简内容。
方法2:通用读取DebuggerDisplay属性的格式化器
如果想复用所有类的DebuggerDisplay配置,无需为每个类型单独写格式化器,可以实现一个通用格式化器,自动解析并执行属性中配置的表达式:
// 在测试初始化阶段注册 FluentAssertions.Formatting.Formatter.Add(new DebuggerDisplayFormatter()); public class DebuggerDisplayFormatter : IValueFormatter { public bool CanHandle(Type type) { return Attribute.IsDefined(type, typeof(DebuggerDisplayAttribute)); } public void Format(object value, FormattedObjectGraph formattedGraph, FormattingContext context, FormatChild formatChild) { var attr = (DebuggerDisplayAttribute)Attribute.GetCustomAttribute(value.GetType(), typeof(DebuggerDisplayAttribute)); if (attr == null) return; var expression = attr.Value.Trim('{', '}'); // 处理调用方法的情况(如{ToDebugString()}) if (expression.EndsWith("()")) { var methodName = expression.Substring(0, expression.Length - 2); var method = value.GetType().GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance); if (method != null && method.ReturnType == typeof(string)) { var result = (string)method.Invoke(value, null); formattedGraph.AddFragment(result); return; } } // 扩展:若需要支持属性引用(如"{A}-{B}"),可通过表达式树解析表达式 // 此处为 fallback,使用默认ToString(可选) formattedGraph.AddFragment(value.ToString()); } }
这个通用格式化器会自动识别带有DebuggerDisplayAttribute的类型,优先执行配置的方法生成精简文本,适配更多场景。
注意事项
- 格式化器必须在断言执行前完成注册,建议放在测试项目的全局初始化代码或单个测试类的
[SetUp]方法中。 - 通用格式化器可根据需求扩展,比如支持
DebuggerDisplay中的属性拼接表达式(如"{A} | {B}"),需通过表达式树解析计算表达式值。
内容的提问来源于stack exchange,提问作者Gaspar Nagy

