You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

C#单元测试中如何忽略顺序比较两个含JSON结构的列表

在C#单元测试中忽略顺序比较包含JSON结构的列表

嘿,这个场景我在做单元测试时经常碰到!要忽略列表元素顺序比较两个包含JSON结构的列表,有几个实用的方案,我给你拆解一下:

方案1:强类型映射+自定义相等比较器(无第三方依赖)

如果你的JSON结构是固定的,最好先把列表映射到强类型实体类,再通过自定义比较器判断元素是否相等,最后验证两个列表的元素集合一致。

步骤1:定义实体类映射JSON结构

public class PartData
{
    public ObjectId _id { get; set; }
    public string PartName { get; set; }
    public decimal? MAP { get; set; }
    public string MPN { get; set; }
    public bool IsActive { get; set; }
    public string Gtin { get; set; }
}

步骤2:实现自定义相等比较器

public class PartDataEqualityComparer : IEqualityComparer<PartData>
{
    public bool Equals(PartData x, PartData y)
    {
        if (x == null && y == null) return true;
        if (x == null || y == null) return false;
        
        // 逐个比较所有属性,确保完全匹配
        return x._id == y._id &&
               x.PartName == y.PartName &&
               x.MAP == y.MAP &&
               x.MPN == y.MPN &&
               x.IsActive == y.IsActive &&
               x.Gtin == y.Gtin;
    }

    public int GetHashCode(PartData obj)
    {
        // 组合所有属性的哈希值,确保相等的对象哈希值一致
        return HashCode.Combine(obj._id, obj.PartName, obj.MAP, obj.MPN, obj.IsActive, obj.Gtin);
    }
}

步骤3:在单元测试中验证

有两种验证方式:

  • 方式A:先排序再用SequenceEqual比较
var sortedDbValues = DBvalues.OrderBy(item => item._id).ToList();
var sortedExcelValues = actualExcelvalues.OrderBy(item => item._id).ToList();

Assert.IsTrue(sortedDbValues.SequenceEqual(sortedExcelValues, new PartDataEqualityComparer()));
  • 方式B:验证元素数量相同且每个元素都存在于另一个列表
Assert.AreEqual(DBvalues.Count, actualExcelvalues.Count);
Assert.IsTrue(DBvalues.All(dbItem => 
    actualExcelvalues.Any(excelItem => new PartDataEqualityComparer().Equals(dbItem, excelItem))));

方案2:动态JSON序列化比较(适合无强类型场景)

如果不想定义实体类,可以通过Newtonsoft.Json(Json.NET)序列化时排序属性,再将数组元素排序后比较JSON字符串。

实现代码

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

// 自定义序列化解析器,确保属性按名称排序
public class OrderedContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        return base.CreateProperties(type, memberSerialization)
                   .OrderBy(p => p.PropertyName)
                   .ToList();
    }
}

// 序列化配置
var serializerSettings = new JsonSerializerSettings
{
    Formatting = Formatting.None,
    ContractResolver = new OrderedContractResolver()
};

// 将列表转为JArray,排序后再序列化
var dbJArray = JArray.Parse(JsonConvert.SerializeObject(DBvalues, serializerSettings));
var excelJArray = JArray.Parse(JsonConvert.SerializeObject(actualExcelvalues, serializerSettings));

var sortedDbJson = JsonConvert.SerializeObject(dbJArray.OrderBy(item => item.ToString(serializerSettings)));
var sortedExcelJson = JsonConvert.SerializeObject(excelJArray.OrderBy(item => item.ToString(serializerSettings)));

// 比较最终的JSON字符串
Assert.AreEqual(sortedDbJson, sortedExcelJson);

方案3:用FluentAssertions框架(最简洁)

如果可以引入第三方测试库,FluentAssertions是单元测试的利器,自带忽略元素顺序的断言方法,代码极其简洁。

步骤1:安装NuGet包

在NuGet包管理器中安装FluentAssertions

步骤2:编写测试断言

如果是强类型列表:

using FluentAssertions;

DBvalues.Should().BeEquivalentTo(actualExcelvalues, options => 
    options.ComparingByMembers<PartData>());

如果是动态JObject列表:

using FluentAssertions;
using Newtonsoft.Json.Linq;

var dbJObjects = DBvalues.Select(item => JObject.FromObject(item)).ToList();
var excelJObjects = actualExcelvalues.Select(item => JObject.FromObject(item)).ToList();

dbJObjects.Should().BeEquivalentTo(excelJObjects);

总结

  • 追求无依赖:选方案1;
  • 动态JSON场景:选方案2;
  • 追求代码简洁、可读性高:强烈推荐方案3,FluentAssertions的断言语法非常直观,还能给出详细的不匹配信息。

内容的提问来源于stack exchange,提问作者Mrunal

火山引擎 最新活动