xUnit Get请求测试失败:Return_Expected_Result_From_API断言不匹配问题求助
解决xUnit Get请求测试中Assert.Equal失败的问题
让我们一步步拆解你的问题:
1. 首先修复预期内容的类型错误
看你的测试代码,expectedContent是List<GetLocationDTO>,但你在里面混入了GetCountryDTO的实例:
var expectedContent = new List<GetLocationDTO> { new GetLocationDTO { Id = 1, Name = "England", ShortName = "Eng" }, new GetCountryDTO { Id = 2, Name = "France", ShortName = "Fr" }, // 类型不匹配 new GetCountryDTO { Id = 3, Name = "Lithuania", ShortName = "LT" } // 类型不匹配 };
即使GetCountryDTO和GetLocationDTO属性结构完全一致,它们是不同的类型。API返回的内容反序列化后会得到GetLocationDTO实例,和你预期里的GetCountryDTO实例根本无法匹配。先把这两个改成GetLocationDTO:
var expectedContent = new List<GetLocationDTO> { new GetLocationDTO { Id = 1, Name = "England", ShortName = "Eng" }, new GetLocationDTO { Id = 2, Name = "France", ShortName = "Fr" }, new GetLocationDTO { Id = 3, Name = "Lithuania", ShortName = "LT" } };
2. 解决引用类型的相等性校验问题
这是核心问题:默认情况下,C#引用类型的Equals方法比较的是对象引用,而不是属性值。即使反序列化得到的GetLocationDTO实例和你预期的实例属性完全一致,它们是内存中的两个不同对象,Assert.Equal就会判定为不相等。
你有几个可行的解决方案:
选项A:使用FluentAssertions库(推荐)
这个库专门为测试断言设计,可以自动比较对象的属性值,用法简洁且可读性高。
- 安装NuGet包:
FluentAssertions - 修改你的断言代码:
public static async Task AssertResponseWithContentAsync<T>(Stopwatch stopwatch, HttpResponseMessage response, HttpStatusCode expectedStatusCode, T expectedContent) { AssertCommonResponseParts(stopwatch, response, expectedStatusCode); Assert.Equal(_jsonMediaType, response.Content.Headers.ContentType?.MediaType); var actualContent = await JsonSerializer.DeserializeAsync<T?>( await response.Content.ReadAsStreamAsync(), _jsonSerializerOptions); // 用FluentAssertions的属性相等断言 actualContent.Should().BeEquivalentTo(expectedContent); }
选项B:给GetLocationDTO重写相等性方法
让DTO自己定义“相等”的规则:
public class GetLocationDTO : IEquatable<GetLocationDTO> { public int Id { get; set; } public string Name { get; set; } public string ShortName { get; set; } public bool Equals(GetLocationDTO? other) { if (other is null) return false; // 比较所有关键属性 return Id == other.Id && Name == other.Name && ShortName == other.ShortName; } // 重写基类方法以保持一致性 public override bool Equals(object? obj) => Equals(obj as GetLocationDTO); public override int GetHashCode() => HashCode.Combine(Id, Name, ShortName); }
这样Assert.Equal就会按照你定义的规则比较属性值,而不是对象引用。
选项C:使用自定义比较器
如果你不想修改DTO类,可以创建一个IEqualityComparer<GetLocationDTO>,然后用SequenceEqual比较集合:
// 定义比较器 public class LocationDtoEqualityComparer : IEqualityComparer<GetLocationDTO> { public bool Equals(GetLocationDTO? x, GetLocationDTO? y) { if (x == null && y == null) return true; if (x == null || y == null) return false; return x.Id == y.Id && x.Name == y.Name && x.ShortName == y.ShortName; } public int GetHashCode(GetLocationDTO obj) { return HashCode.Combine(obj.Id, obj.Name, obj.ShortName); } } // 修改断言代码 var actualContent = await JsonSerializer.DeserializeAsync<List<GetLocationDTO>>( await response.Content.ReadAsStreamAsync(), _jsonSerializerOptions); Assert.True(actualContent.SequenceEqual(expectedContent, new LocationDtoEqualityComparer()));
总结
先修正预期内容的类型错误,再选择一种适合你的方式解决引用类型的相等性问题。个人推荐FluentAssertions,它能让你的测试断言更易读,还能轻松处理复杂对象的嵌套比较。
内容的提问来源于stack exchange,提问作者sr546
相关产品推荐
相关产品推荐

