1. 概要

この記事では、 JSONAssertライブラリを見ていきます。これは、JSONデータを理解し、そのデータを使用して複雑なJUnitテストを作成することに重点を置いたライブラリです。

2. Mavenの依存関係

まず、Mavenの依存関係を追加しましょう。

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

ライブラリの最新バージョンこちらをチェックしてください。

3. 単純なJSONデータの操作

3.1. LENIENTモードの使用

簡単なJSON文字列の比較からテストを始めましょう。

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

テストは期待されるJSON文字列として合格し、実際のJSON文字列は同じです。

モードLENIENTの比較は、実際のJSONに拡張フィールドが含まれている場合でも、テストに合格することを意味します。

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

ご覧のとおり、 real 変数には、予期されるStringには存在しない追加のフィールドzipが含まれています。 それでも、テストは合格します。

この概念は、アプリケーション開発に役立ちます。 これは、既存のテストを中断することなく、APIを拡張し、必要に応じて追加のフィールドを返すことができることを意味します。

3.2. STRICTモードの使用

前のサブセクションで説明した動作は、STRICT比較モードを使用して簡単に変更できます。

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

上記の例では、 assertNotEquals()を使用していることに注意してください。

3.3. JSONCompareModeの代わりにBooleanを使用する

比較モードは、JSONCompareModeの代わりにbooleanを使用するオーバーロードメソッドを使用して定義することもできます。ここで、 LENIENT =falseおよびSTRICT= 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. 論理比較

前述のように、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);

厳密かどうかにかかわらず、上記のテストはどちらの場合も合格します。

論理比較の別の例は、同じ値に対して異なるタイプを使用することで示すことができます。

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);

ここで最初に注意することは、前の例で使用したように、文字列の代わりにJSONObjectを使用していることです。 次は、expectedにはIntegerを使用し、actualにはDoubleを使用しました。 両方の論理値12345が同じであるため、タイプに関係なくテストに合格します。

ネストされたオブジェクト表現がある場合でも、このライブラリは非常にうまく機能します。

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. ユーザー指定のメッセージによるアサーション

すべての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);
}

障害が発生した場合は、エラーメッセージ全体がより意味をなします。

Only one field is expected: name 
Unexpected: id

1行目はユーザー指定のメッセージで、2行目はライブラリによって提供される追加メッセージです。

4. JSON配列の操作

JSON配列の比較ルールは、JSONオブジェクトとは少し異なります。

4.1. 配列内の要素の順序

最初の違いは、配列内の要素の順序は、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);

これは、APIが並べ替えられた要素の配列を返し、応答が並べ替えられているかどうかを確認するシナリオで非常に役立ちます。

4.2. 配列内の拡張要素

もう1つの違いは、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);

上記の例は、 LENIENT 比較モードでも、期待される配列の項目が実際の配列の項目と正確に一致する必要があることを明確に示しています。 単一の要素であっても、追加または削除すると失敗します。

4.3. アレイ固有の操作

また、アレイの内容をさらに検証するためのテクニックもいくつかあります。

配列のサイズを確認したいとします。 これは、期待値として具体的な構文を使用することで実現できます。

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

String “ {names:[4]}” は、配列の予想サイズを指定します。

別の比較手法を見てみましょう。

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

上記の例では、配列内のすべての要素の値が[1,5]で、1と5の両方が含まれている必要があることを確認しています。 1未満または5より大きい値がある場合、上記のテストは失敗します。

5. 高度な比較例

APIが複数のidを返し、それぞれがInteger値であるユースケースを考えてみましょう。 これは、すべてのidが単純な正規表現’\d‘を使用して検証できることを意味します。

上記の正規表現は、 CustomComparator と組み合わせて、すべてのidのすべての値に適用できます。 id のいずれかが正規表現と一致しない場合、テストは失敗します。

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"))));

上記の例の「{id:x}」はプレースホルダーにすぎません。xは何にでも置き換えることができます。 正規表現パターン「\d」が適用される場所です。 id自体は別のフィールドentry内にあるため、Customizationidの位置を指定し、CustomComparatorがは比較を実行できます。

6. 結論

この簡単な記事では、JSONAssertが役立つさまざまなシナリオについて説明しました。 非常に単純な例から始めて、より複雑な比較に移りました。

もちろん、いつものように、ここで説明するすべての例の完全なソースコードは、GitHubにあります。