为什么 Assert.Equal 两个不同的 JObject(Newtonsoft.Json.Linq.JObject) 能够通过

问题描述 投票:0回答:2

我对 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 调用哪个比较器。

c# json.net xunit
2个回答
11
投票

这是 xUnit 断言 + Newtonsoft Json.NET 的一个错误。原因有两个:

  • Newtonsoft方面:
    • JToken
      (库中“所有内容”的基本类型)实现
      IEnumerable
    • JValue
      实现
      IComparable
      (和
      IEnumerable
      ,因为它继承自
      JToken
      ),当两个
      JValue
      不兼容时会抛出异常
  • 从 xUnit 方面 - 如果类型实现
    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
}

备注:


0
投票

考虑到它是用于测试代码,因此在断言的 JSON 正文中报告错误节点位置和原因会更好。有一个 Xunit JSON 扩展package可以执行与以下类似的操作,因此不需要任何解决方法:

JsonAssertion.Equivalent("""
                {
                  "foo": "bar"
                }
                """,
                """
                {
                  "foo": 1
                }
                """);

(我是该包的作者。)

© www.soinside.com 2019 - 2024. All rights reserved.