You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 13:20:37