使用NUnit按类型筛选记录属性进行断言(排除指定属性)
基于数据类型筛选Record属性进行NUnit断言的方案
你可以通过反射结合NUnit断言实现按数据类型筛选Record属性进行比较,下面是几种实用的实现方式:
方法一:自定义约束(Constraint)实现类型筛选比较
创建自定义NUnit约束,自动筛选指定类型的属性完成比对:
using System.Reflection; using NUnit.Framework.Constraints; public class PropertiesOfTypeConstraint : Constraint { private readonly object _expected; private readonly Type[] _targetTypes; public PropertiesOfTypeConstraint(object expected, params Type[] targetTypes) { _expected = expected; _targetTypes = targetTypes; } public override ConstraintResult ApplyTo<TActual>(TActual actual) { if (_expected == null && actual == null) return new ConstraintResult(this, actual, ConstraintStatus.Success); if (_expected == null || actual == null) return new ConstraintResult(this, actual, ConstraintStatus.Failure); var actualType = actual.GetType(); var expectedType = _expected.GetType(); // 筛选所有匹配目标类型的公共属性 var properties = actualType.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => _targetTypes.Contains(p.PropertyType)) .ToList(); var failures = new List<string>(); foreach (var prop in properties) { var actualValue = prop.GetValue(actual); var expectedValue = prop.GetValue(_expected); if (!Equals(actualValue, expectedValue)) { failures.Add($"属性 {prop.Name}: 预期值 {expectedValue}, 实际值 {actualValue}"); } } return new ConstraintResult( this, actual, failures.Count == 0 ? ConstraintStatus.Success : ConstraintStatus.Failure) { Message = failures.Any() ? string.Join("\n", failures) : string.Empty }; } } // 扩展方法简化调用 public static class ConstraintExtensions { public static PropertiesOfTypeConstraint PropertiesOfType(this EqualConstraint constraint, params Type[] types) { return new PropertiesOfTypeConstraint(constraint.Expected, types); } }
使用示例:
// 仅比对int和string类型的属性 Assert.That(result, Is.EqualTo(employeeRecord).PropertiesOfType(typeof(int), typeof(string)));
方法二:用Assert.Multiple批量断言指定类型属性
无需自定义约束,通过反射筛选属性后,用Assert.Multiple批量执行断言,可一次性输出所有不匹配的属性:
var targetTypes = new[] { typeof(int), typeof(string) }; var properties = typeof(Employee).GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => targetTypes.Contains(p.PropertyType)) .ToList(); Assert.Multiple(() => { foreach (var prop in properties) { var actualValue = prop.GetValue(result); var expectedValue = prop.GetValue(employeeRecord); Assert.That(actualValue, Is.EqualTo(expectedValue), $"属性 {prop.Name} 不匹配"); } });
方法三:封装辅助工具类
若这类需求频繁出现,可封装工具类简化调用:
public static class RecordAssert { public static void PropertiesOfType<T>(T actual, T expected, params Type[] targetTypes) { if (actual == null && expected == null) return; if (actual == null || expected == null) Assert.Fail("实际或预期的Record实例为null"); var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => targetTypes.Contains(p.PropertyType)) .ToList(); Assert.Multiple(() => { foreach (var prop in properties) { var actualValue = prop.GetValue(actual); var expectedValue = prop.GetValue(expected); Assert.That(actualValue, Is.EqualTo(expectedValue), $"属性 {prop.Name} 不匹配"); } }); } }
调用示例:
RecordAssert.PropertiesOfType(result, employeeRecord, typeof(int), typeof(string));
为什么不推荐用ToString()比较?
你提到的ToString()方式存在明显缺陷:
- 丢失类型语义,比如
double的精度差异可能在字符串中无法体现,或格式化规则不同导致误判 - Record的
ToString()输出受属性顺序、重写逻辑影响,稳定性差 - 无法精准定位具体不匹配的属性,排查问题效率低
内容的提问来源于stack exchange,提问作者buneShot
相关产品推荐
相关产品推荐

