Introduction to JSONassert – JSONassert简介

最后修改: 2017年 3月 23日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll have a look at the JSONAssert library – a library focused on understanding JSON data and writing complex JUnit tests using that data.

在本文中,我们将看看JSONAssert库 – 一个专注于理解JSON数据并使用该数据编写复杂JUnit测试的库。

2. Maven Dependency

2.Maven的依赖性

First, let’s add the Maven dependency:

首先,我们来添加Maven的依赖性。

<dependency>
    <groupId>org.skyscreamer</groupId>
    <artifactId>jsonassert</artifactId>
    <version>1.5.0</version>
</dependency>

Please check out the latest version of the library here.

请查看最新版本的图书馆这里

3. Working With Simple JSON Data

3.使用简单的JSON数据工作

3.1. Using the LENIENT Mode

3.1.使用LENIENT模式

Let’s start our tests with a simple JSON string comparison:

让我们用一个简单的JSON字符串比较来开始我们的测试。

String actual = "{id:123, name:\"John\"}";
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);

The test will pass as the expected JSON string, and the actual JSON string are the same.

测试将通过,因为预期的JSON字符串和实际的JSON字符串是一样的。

The comparison mode LENIENT means that even if the actual JSON contains extended fields, the test will still pass:

比较模式LENIENT意味着,即使实际的JSON包含扩展字段,测试仍将通过:

String actual = "{id:123, name:\"John\", zip:\"33025\"}";
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);

As we can see, the real variable contains an additional field zip which is not present in the expected String. Still, the test will pass.

我们可以看到,real变量包含一个额外的字段zip,这个字段不在预期的String中。不过,测试还是会通过。

This concept is useful in the application development. This means that our APIs can grow, returning additional fields as required, without breaking the existing tests.

这个概念在应用开发中是很有用的。这意味着我们的API可以增长,根据需要返回额外的字段,而不会破坏现有的测试。

3.2. Using the STRICT Mode

3.2.使用STRICT模式

The behavior mentioned in the previous sub-section can be easily changed by using the STRICT comparison mode:

通过使用STRICT比较模式,可以很容易地改变上一小节中提到的行为。

String actual = "{id:123,name:\"John\"}";
JSONAssert.assertNotEquals(
  "{name:\"John\"}", actual, JSONCompareMode.STRICT);

Please note the use of assertNotEquals() in the above example.

请注意上述例子中使用了assertNotEquals()

3.3. Using a Boolean Instead of JSONCompareMode

3.3.使用Boolean而不是JSONCompareMode

The compare mode can also be defined by using an overloaded method that takes boolean instead of JSONCompareMode where LENIENT = false and STRICT = true:

比较模式也可以通过使用一个重载方法来定义,该方法采用boolean代替JSONCompareMode,其中LENIENT = falseSTRICT = true

String actual = "{id:123,name:\"John\",zip:\"33025\"}";
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, false);

actual = "{id:123,name:\"John\"}";
JSONAssert.assertNotEquals(
  "{name:\"John\"}", actual, JSONCompareMode.STRICT);
JSONAssert.assertNotEquals(
  "{name:\"John\"}", actual, true);

3.4. The Logical Comparison

3.4.逻辑上的比较

As described earlier, JSONAssert makes a logical comparison of the data. This means that the ordering of elements does not matter while dealing with JSON objects:

如前所述,JSONAssert对数据进行了逻辑比较。这意味着,在处理JSON对象时,元素的排序并不重要。

String result = "{id:1,name:\"John\"}";
JSONAssert.assertEquals(
  "{name:\"John\",id:1}", result, JSONCompareMode.STRICT);
JSONAssert.assertEquals(
  "{name:\"John\",id:1}", result, JSONCompareMode.LENIENT);

Strict or not, the above test will pass in both the cases.

无论是否严格,上述测试在两种情况下都会通过。

Another example of logical comparison can be demonstrated by using different types for the same value:

逻辑比较的另一个例子可以通过使用不同类型的相同值来证明。

JSONObject expected = new JSONObject();
JSONObject actual = new JSONObject();
expected.put("id", Integer.valueOf(12345));
actual.put("id", Double.valueOf(12345));

JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT);

The first thing to note here is that we are using JSONObject instead of a String as we did for earlier examples. The next thing is that we have used Integer for expected and Double for actual. The test will pass irrespective of the types because the logical value 12345 for both of them is same.

首先要注意的是,我们使用的是JSONObject,而不是像之前的例子那样使用String。其次是我们用Integer表示预期,用Double表示实际。无论哪种类型,测试都会通过,因为两者的逻辑值12345是一样的。

Even in the case when we have nested object representation, this library works pretty well:

即使在我们有嵌套对象表示的情况下,这个库也能很好地工作。

String result = "{id:1,name:\"Juergen\", 
  address:{city:\"Hollywood\", state:\"LA\", zip:91601}}";
JSONAssert.assertEquals("{id:1,name:\"Juergen\", 
  address:{city:\"Hollywood\", state:\"LA\", zip:91601}}", result, false);

3.5. Assertions With User Specified Messages

3.5.带有用户指定信息的断言

All the assertEquals() and assertNotEquals() methods accept a String message as the first parameter. This message provides some customization to our test cases by providing a meaningful message in the case of test failures:

所有的assertEquals()assertNotEquals()方法都接受一个String消息作为第一个参数。这个消息通过在测试失败的情况下提供一个有意义的消息,为我们的测试案例提供一些定制。

String actual = "{id:123,name:\"John\"}";
String failureMessage = "Only one field is expected: name";
try {
    JSONAssert.assertEquals(failureMessage, 
      "{name:\"John\"}", actual, JSONCompareMode.STRICT);
} catch (AssertionError ae) {
    assertThat(ae.getMessage()).containsIgnoringCase(failureMessage);
}

In the case of any failure, the entire error message will make more sense:

在任何失败的情况下,整个错误信息将更有意义。

Only one field is expected: name 
Unexpected: id

The first line is the user specified message and the second line is the additional message provided by the library.

第一行是用户指定的信息,第二行是由库提供的额外信息。

4. Working With JSON Arrays

4.使用JSON数组工作

The comparison rules for JSON arrays differ a little, compared to JSON objects.

与JSON对象相比,JSON数组的比较规则有一点不同。

4.1. The Order of the Elements in an Array

4.1.数组中元素的顺序

The first difference is that the order of elements in an array has to be exactly same in STRICT comparison mode. However, for LENIENT comparison mode, the order does not matter:

第一个区别是,STRICT比较模式下,数组中元素的顺序必须完全相同。然而,对于LENIENT比较模式,顺序并不重要。

String result = "[Alex, Barbera, Charlie, Xavier]";
JSONAssert.assertEquals(
  "[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.LENIENT);
JSONAssert.assertEquals(
  "[Alex, Barbera, Charlie, Xavier]", result, JSONCompareMode.STRICT);
JSONAssert.assertNotEquals(
  "[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.STRICT);

This is pretty useful in the scenario where the API returns an array of sorted elements, and we want to verify if the response is sorted.

在API返回一个排序元素的数组的情况下,这是相当有用的,我们想验证响应是否是排序的。

4.2. The Extended Elements in an Array

4.2.阵列中的扩展元素

Another difference is that extended elements are not allowed when the dealing with JSON arrays:

另一个区别是,在处理JSON数组时,不允许扩展元素

String result = "[1,2,3,4,5]";
JSONAssert.assertEquals(
  "[1,2,3,4,5]", result, JSONCompareMode.LENIENT);
JSONAssert.assertNotEquals(
  "[1,2,3]", result, JSONCompareMode.LENIENT);
JSONAssert.assertNotEquals(
  "[1,2,3,4,5,6]", result, JSONCompareMode.LENIENT);

The above example clearly demonstrates that even with the LENIENT comparison mode, the items in the expected array has to match the items in the real array exactly. Adding or removing, even a single element, will result in a failure.

上面的例子清楚地表明,即使使用LENIENT比较模式,预期数组中的项目必须与实际数组中的项目完全匹配。添加或删除,哪怕是一个元素,都会导致失败。

4.3. Array Specific Operations

4.3.阵列的具体操作

We also have a couple of other techniques to verify the contents of the arrays further.

我们还有一些其他技术来进一步验证数组的内容。

Suppose we want to verify the size of the array. This can be achieved by using a concrete syntax as the expected value:

假设我们想验证数组的大小。这可以通过使用一个具体的语法作为预期值来实现。

String names = "{names:[Alex, Barbera, Charlie, Xavier]}";
JSONAssert.assertEquals(
  "{names:[4]}", 
  names, 
  new ArraySizeComparator(JSONCompareMode.LENIENT));

The String “{names:[4]}” specifies the expected size of the array.

String “{names:[4]}”指定数组的预期大小。

Let’s have a look at another comparison technique:

让我们来看看另一种比较技术。

String ratings = "{ratings:[3.2,3.5,4.1,5,1]}";
JSONAssert.assertEquals(
  "{ratings:[1,5]}", 
  ratings, 
  new ArraySizeComparator(JSONCompareMode.LENIENT));

The above example verifies that all the elements in the array must have a value between [1,5], both 1 and 5 inclusive. If there is any value less than 1 or greater than 5, the above test will fail.

上面的例子验证了数组中所有元素的值必须在[1,5]之间,包括1和5在内。如果有任何小于1或大于5的值,上述测试将失败。

5. Advanced Comparison Example

5.高级比较实例

Consider the use case where our API returns multiple ids, each one being an Integer value. This means that all the ids can be verified using a simple regular expression ‘\d‘.

考虑这样的用例:我们的API返回多个ids,每个都是一个Integer值。这意味着所有的ids都可以用一个简单的正则表达式’\d‘来验证。

The above regex can be combined with a CustomComparator and applied to all the values of all the ids. If any of the ids does not match the regex, the test will fail:

上面的重合词可以与CustomComparator结合,并应用于所有ids的所有值。如果任何一个ids不符合该重合词,测试将失败。

JSONAssert.assertEquals("{entry:{id:x}}", "{entry:{id:1, id:2}}", 
  new CustomComparator(
  JSONCompareMode.STRICT, 
  new Customization("entry.id", 
  new RegularExpressionValueMatcher<Object>("\\d"))));

JSONAssert.assertNotEquals("{entry:{id:x}}", "{entry:{id:1, id:as}}", 
  new CustomComparator(JSONCompareMode.STRICT, 
  new Customization("entry.id", 
  new RegularExpressionValueMatcher<Object>("\\d"))));

The “{id:x}” in the above example is nothing but a placeholder – the x can be replaced by anything. As it is the place where the regex pattern ‘\d‘ will be applied. Since the id itself is inside another field entry, the Customization specifies the position of the id, so that the CustomComparator can perform the comparison.

上例中的”{id:x}“只是一个占位符–x可以被任何东西取代。因为它是重码模式’d‘将被应用的地方。由于id本身在另一个字段entry内,Customization指定了id的位置,以便CustomComparator可以执行比较。

6. Conclusion

6.结论

In this quick article, we looked at various scenarios where JSONAssert can be helpful. We started with a super simple example and moved to more complex comparisons.

在这篇快速的文章中,我们看了JSONAssert有帮助的各种情况。我们从一个超级简单的例子开始,然后转到更复杂的比较。

Of course, as always, the full source code of all the examples discussed here can be found over on GitHub.

当然,像往常一样,这里讨论的所有例子的完整源代码可以在GitHub上找到over