Kotest JSON マッチャー(JSON Matchers)

JSON マッチャーについて説明する。

概要

JSON マッチャーを使用するには、ビルドに testImplementation("io.kotest.extensions:kotest-assertions-json:<version>") を追加する必要がある。

基本マッチャー

マッチャー 説明 対象
shouldBeValidJson 指定した文字列が有効な JSON としてパースできることを確認する。 Multiplatform
shouldBeJsonObject 文字列が有効な JSON オブジェクトであることを確認する。 Multiplatform
shouldBeJsonArray 文字列が有効な JSON 配列であることを確認する。 Multiplatform

コンテンツベースのマッチング

詳しくは JSON コンテンツマッチングを参照する。

マッチャー 説明 対象
shouldEqualJson 文字列が指定した JSON 構造と一致することを確認する。 Multiplatform
shouldEqualSpecifiedJson 文字列が指定した JSON 構造と一致することを確認するが、指定されていない追加プロパティを許可する。 Multiplatform
shouldContainJsonKey 文字列が JSON であり、指定した JSON パスを含むことを確認する。 JVM
shouldContainJsonKeyValue 文字列が JSON であり、指定した値を持つ指定 JSON パスを含むことを確認する。 JVM
shouldMatchJsonResource 文字列が指定したテストリソースの JSON コンテンツと一致することを確認する。 JVM

スキーマ検証

マッチャー 説明 対象
shouldMatchSchema String または kotlinx.serialization.JsonElementJsonSchema と一致することを確認する。スキーマ構成の詳細は以下を参照する。 Multiplatform

JSON コンテンツマッチング

このモジュールは JVM および JS ターゲットで使用できる。

shouldEqualJson

json.shouldEqualJson(other) は、左側が右側と同じ JSON 構造であることを確認する。

このマッチャーは、異なるフォーマットや異なるキー順序を許可する。

たとえば、次の 2 つの JSON 文字列は同じものと見なされる。

{
   "name": "sam",
   "location": "chicago",
   "age" : 41
}

そして

{ "age" : 41, "name": "sam", "location": "chicago" }

このマッチャーの反対は shouldNotEqualJson であり、2 つの JSON 文字列が同じものと見なされる場合にエラーになる。

compareJsonOptions

shouldEqualJson は、JSON 比較の動作を切り替えるためのフラグを持つ CompareJsonOptions 型の追加パラメータをサポートする。

使用法:
オプションは次のようにインラインで指定できる。

a.shouldEqualJson(b, compareJsonOptions { arrayOrder = ArrayOrder.Strict })

もう 1 つの方法は、必要な比較動作を定義することである。

val myOptions = compareJsonOptions {
   typeCoercion = TypeCoercion.Enabled
   arrayOrder = ArrayOrder.Lenient
}

infix fun String.lenientShouldEqualJson(other: String) = this.shouldEqualJson(other, myOptions)

"[1, 2]" lenientShouldEqualJson "[2, 1]" // This will pass
Parameters
名前 目的 可能な値 デフォルト
PropertyOrder 比較時に JSON オブジェクトのプロパティ順序を考慮するかを決定する。 PropertyOrder.Strict, PropertyOrder.Lenient PropertyOrder.Lenient; プロパティ順序は重要ではない。
ArrayOrder 比較時に JSON 配列の要素順序を考慮するかを決定する。 ArrayOrder.Strict, ArrayOrder.Lenient ArrayOrder.Strict; 要素順序は重要である。
FieldComparison actualJSONexpected と比較して追加プロパティを含む場合に比較を失敗させるかを決定する。 FieldComparison.Strict, FieldComparison.Lenient FieldComparison.Strict; 追加プロパティによって不一致になる。
NumberFormat 数値形式について厳密に比較するかを決定する。たとえば 100.0100 を同じと見なすかどうかである。 NumberFormat.Strict, NumberFormat.Lenient NumberFormat.Lenient; 数値形式は重要ではない。
TypeCoercion 文字列に数値やブール値が含まれる場合などに、型を強制変換するかを決定する。 TypeCoercion.Enabled, TypeCoercion.Disabled TypeCoercion.Disabled; 型は強制変換されない。

対象: Multiplatform

shouldEqualSpecifiedJson

shouldEqualJson の別名で、デフォルトオプションは FieldComparison だけが異なり、FieldComparison.Lenient に設定される。

val a = """ { "a": true, "date": "2019-11-03" } """
val b = """ { "a": true } """

// this would pass
a shouldEqualSpecifiedJson b

// this would fail
a shouldEqualJson b

対象: Multiplatform

shouldContainJsonKey

json.shouldContainJsonKey("$.json.path") は、JSON 文字列に指定した JSON パスが含まれることを確認する。

反対のマッチャーは shouldNotContainJsonKey で、JSON 文字列に指定した JSON パスが含まれる場合にエラーになる。

対象: JVM

shouldContainJsonKeyValue

str.shouldContainJsonKeyValue("$.json.path", value) は、JSON 文字列に特定の値を持つ JSON パスが含まれることを確認する。

反対のマッチャーは shouldNotContainJsonKeyValue で、指定した JSON パスに指定した値が含まれる場合にエラーになる。

対象: JVM

shouldMatchJsonResource

json.shouldMatchJsonResource("/file.json") は、プロパティ順序とフォーマットを無視して、JSON が既存のテストリソース /file.json と同じであることを確認する。

対象: JVM

JSON スキーママッチャー

マッチャー 説明 対象
shouldMatchSchema String または kotlinx.serialization.JsonElementJsonSchema と一致することを確認する。スキーマ構成の詳細は以下を参照する。 Multiplatform

JSON Schema のサブセットは、テキストスキーマをパースして定義できる。

例:

val parsedSchema = parseSchema(
  """
  {
  "$id": "https://example.com/geographical-location.schema.json",  // will  be ignored
  "$schema": "https://json-schema.org/draft/2020-12/schema",       // will be ignored
  "title": "Longitude and Latitude Values",                        // will be ignored
  "description": "A geographical coordinate.",                     // will be ignored
  "required": [ "latitude", "longitude" ],
  "type": "object",
  "properties": {
    "latitude": {
      "type": "number",
      "minimum": -90,
      "maximum": 90
    },
    "longitude": {
      "type": "number",
      "minimum": -180,
      "maximum": 180
    }
  }
}
  """
)

または Kotest の組み込み DSL を使用する。

val addressSchema = jsonSchema {
  obj {   // object is reserved, obj was chosen over jsonObject for brevity but could be changed ofc, or jsonObject could be added as alternative.
    withProperty("street", required = true) { string() }
    withProperty("zipCode", required = true) {
      integer {
        beEven() and beInRange(10000..99999)   // supports constructing a matcher that will be used to test values
      }
    }
    additionalProperties = false   // triggers failure if other properties are defined in actual
  }
}

val personSchema = jsonSchema {
  obj {
    withProperty("name", required = true) { string() }
    withProperty("address") { addressSchema() } // Schemas can re-use other schemas 🎉
  }
}

スキーマ構築

配列

配列は順序付きの要素に使用される。JSON では、配列の各要素が異なる型を持つことができる。

Length (minItems and maxItems)

配列の長さは minItemsmaxItems キーワードで指定できる。各キーワードの値は非負の数でなければならず、デフォルトは 0 と Int.MAX_VALUE である。

val lengthBoundedSchema = jsonSchema {
  array(minItems = 0, maxItems = 1) { number() }
}
Uniqueness

スキーマは、配列の各項目が一意であることを確認できる。uniqueItems キーワードを true に設定するだけでよい。

val uniqueArray = jsonSchema {
  array(uniqueItems = true) { number() }
}

⚠️ Kotest は現在、JSON Schema のサブセットのみをサポートしている。現在サポートされていない項目は次のとおりである。

  • $defs and $refs
  • Recursive schemas
  • Parsing of schema composition
  • string.format
  • array.prefixItems,
  • array.contains,
  • array.items = false
  • array.maxContains
  • array.minContains
  • array.uniqueItems
  • enum

検証

スキーマが定義されると、それを基準に Stringkotlinx.serialization.JsonElement を検証できる。

"{}" shouldMatchSchema personSchema

// fails with:
// $.name => Expected string, but was undefined

""" { "name": "Emil", "age": 34 } """
// Passes, since address isn't required and `additionalProperties` are allowed

参照