如何在JUnit测试中执行自定义JSON比较?

时间:2018-03-12 23:39:49

标签: java json junit

我有一个名为Info的JSON对象,它们只有字符串&看起来像这样:

{
  "prop1":      "value1",
  "prop2":      "value1",
  "timestamp":  "2018-02-28T05:30:10.100Z",
  "prop4":      "value4",
  "prop_N":     "I have a total of 10 properties."
}

我想将它们与预期的JSON对象进行比较。

为此,我将预期的和实际的JSON转换为Java对象/ POJO。我期望的JSON可以在4个属性中的任何一个中具有空值。如果任何预期的JSON字段为null,那么我希望我的代码不比较这些字段。如果它们不为空,则使用String.equals()进行比较,但有一个例外:对于始终为UTC的timestamp,我们必须忽略秒和&毫秒。

比较结果将用于JUnit断言方法,以检查收到的实际JSON是否与预期的JSON匹配。

我已经实现了如下所示的代码。但是,有两个问题:

  1. 它不会告诉您哪些字段不匹配(它只返回true / false)
  2. 由于多个if块,它非常难看。
  3. 有人可以建议如何解决这些问题吗?

    public boolean matchesInfo(Info givenInfo) throws Exception {
    
        if (this.getProp1() != null) {
            boolean prop1Matching = this.getProp1().equals(givenInfo.getProp1());
            if (prop1Matching == false){return false;}
        }
    
        // More if blocks like this. Very long and ugly.
    
        // For timestamp comparison, call a custom function to strip seconds, milliseconds.
    }
    

2 个答案:

答案 0 :(得分:1)

不是将JSON反序列化为对象以便于比较,而是可以使用JsonUnit来促进测试用例中有意义的“JSON比较”。

下面有一些例子可以解决你问题中的观点...显示有意义的回答(即命名不匹配的字段/值),忽略空/缺少字段。还有更多详细信息in the docs,包括使用自定义匹配器来处理此要求:“对于始终为UTC的时间戳,我们必须忽略秒和毫秒。”。

@Test
public void canAssertJsonEquality() {
    String expected = "{\n" +
            "  \"prop1\": \"value1\",\n" +
            "  \"prop2\": \"value1\"\n" +
            "}";
    String actual = "{\n" +
            "  \"prop1\": \"value1\",\n" +
            "  \"prop2\": \"value1\"\n" +
            "}";

    JsonAssert.assertJsonEquals(expected, actual);
}

@Test
public void canAssertJsonEqualityRegardlessOfPropertyOrder() {
    String expected = "{\n" +
            "  \"prop1\": \"value1\",\n" +
            "  \"prop2\": \"value1\"\n" +
            "}";
    String actual = "{\n" +
            "  \"prop2\": \"value1\",\n" +
            "  \"prop1\": \"value1\"\n" +
            "}";

    JsonAssert.assertJsonEquals(expected, actual);
}

@Test
public void canIgnoreFieldsWhichAreInActualButNotInExpected() {
    String expected = "{\n" +
            "  \"prop2\": \"value1\"\n" +
            "}";
    String actual = "{\n" +
            "  \"prop1\": \"value1\",\n" +
            "  \"prop2\": \"value1\"\n" +
            "}";

    JsonAssert.assertJsonEquals(expected, actual, JsonAssert.when(Option.IGNORING_EXTRA_FIELDS));
} 

// fails with ...
//      Different keys found in node "", expected: <[prop1, prop2]> but was: <[prop2]>. Missing: "prop1"
@Test
public void willFailIfActualIsMissingAProperty() {
    String expected = "{\n" +
            "  \"prop1\": \"value1\",\n" +
            "  \"prop2\": \"value1\"\n" +
            "}";
    String actual = "{\n" +
            "  \"prop2\": \"value1\"\n" +
            "}";

    JsonAssert.assertJsonEquals(expected, actual);
}

// fails with ...
//      Different value found in node "prop1", expected: <"value1"> but was: <"value2">.
@Test
public void willFailIfActualHasAnIncorrectValue() {
    String expected = "{\n" +
            "  \"prop1\": \"value1\",\n" +
            "  \"prop2\": \"value1\"\n" +
            "}";
    String actual = "{\n" +
            "  \"prop1\": \"value2\",\n" +
            "  \"prop2\": \"value1\"\n" +
            "}";

    JsonAssert.assertJsonEquals(expected, actual);
} 

答案 1 :(得分:0)

有几种方法可以使用 ModelAssert - https://github.com/webcompere/model-assert 解决此问题。

首先,我们可以断言我们感兴趣的字段:

assertJson(actual)
   .at("/prop1").isText("value1")
   .at("/prop2").isText("value2");

或者,我们可以使用 isEqualTo 方法,将某些路径标记为忽略

assertJson(actual)
   .where()
     .at("/prop2").isIgnored()
     .at("/prop3").isIgnored()
   .isEqualTo(expected);

assertJson 将接受 StringJsonNode 甚至是 Map/POJO,它在比较之前将其序列化为 JSON。同样,isEqualTo 也允许这样做。

ModelAssert 还支持 YML,并且可以将其断言表示为 Hamcrest 匹配器。它解释了事情失败时的差异。