我对 c#、xUnit 以及 Newtonsoft.Json 都很陌生。当我尝试在单元测试中使用 Assert.Equal() 方法比较两个不同的 JOject 时,它通过了,请参阅下面的示例代码
using Newtonsoft.Json.Linq;
namespace TestProject1
{
public class UnitTest1
{
[Fact]
public void Test1()
{
JObject jobj1 = JObject.FromObject(new { foo = "bar" });
JObject jobj2 = JObject.FromObject(new { foo = 1 });
JObject jobj3 = JObject.FromObject(new { foo = "b" });
Assert.Equal(jobj1, jobj2); // output: pass
Assert.Equal(jobj1, jobj3); // output: failure
Assert.True(jobj1.Equals(jobj2)); // output: failure
Assert.True(jobj1.Equals(jobj3)); // output: failure
}
}
}
我不太明白这是怎么发生的,我应该深入研究 xUnit 还是 Newtonsoft.Json?
我发现 Newtonsoft.Json 有一个 DeepEquals 方法,但我不确定 xUnit 调用哪个比较器。
这是 xUnit 断言 + Newtonsoft Json.NET 的一个错误。原因有两个:
JToken
(库中“所有内容”的基本类型)实现 IEnumerable
JValue
实现 IComparable
(和 IEnumerable
,因为它继承自 JToken
),当两个 JValue
不兼容时会抛出异常IComparable
并且比较器抛出错误,错误将被忽略,那么如果类型实现 IEnumerable
,它将被视为集合,而 JValue
是一个空集合,因此它们是集合相等最小重现如下:
[Fact]
public void Test2()
{
var jvLeft = JToken.FromObject(1);
var jvRight = JToken.FromObject("bar");
// some "debug" checks
Assert.True(jvLeft is JValue);
Assert.Empty(jvLeft);
Assert.Throws<FormatException>(() => (jvRight as IComparable).CompareTo(jvLeft));
Assert.Equal(jvRight, jvLeft); // output: pass
}
修复方法之一是使用
Assert.StrictEqual
(仅适用于 JValue
):
[Fact]
public void Test2()
{
var jvLeft = JToken.FromObject(1);
var jvRight = JToken.FromObject("bar");
Assert.StrictEqual(jvRight, jvLeft); // output: fail
}
或者使用/跟进
JToken.DeepEquals
(正如您所发现的):
[Fact]
public void Test12()
{
JObject jobj1 = JObject.FromObject(new { foo = "bar" });
JObject jobj2 = JObject.FromObject(new { foo = 1 });
Assert.Equal(jobj1, jobj2); // output: pass
Assert.True(JToken.DeepEquals(jobj1, jobj2)); // output: fail
}
备注:
考虑到它是用于测试代码,因此在断言的 JSON 正文中报告错误节点位置和原因会更好。有一个 Xunit JSON 扩展package可以执行与以下类似的操作,因此不需要任何解决方法:
JsonAssertion.Equivalent("""
{
"foo": "bar"
}
""",
"""
{
"foo": 1
}
""");
(我是该包的作者。)