001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.test.json; 018 019import java.io.File; 020import java.io.InputStream; 021import java.nio.charset.Charset; 022import java.util.List; 023import java.util.Map; 024 025import com.jayway.jsonpath.JsonPath; 026import org.assertj.core.api.AbstractAssert; 027import org.assertj.core.api.AbstractBooleanAssert; 028import org.assertj.core.api.AbstractCharSequenceAssert; 029import org.assertj.core.api.AbstractObjectAssert; 030import org.assertj.core.api.Assert; 031import org.assertj.core.api.Assertions; 032import org.assertj.core.api.ListAssert; 033import org.assertj.core.api.MapAssert; 034import org.skyscreamer.jsonassert.JSONCompare; 035import org.skyscreamer.jsonassert.JSONCompareMode; 036import org.skyscreamer.jsonassert.JSONCompareResult; 037import org.skyscreamer.jsonassert.comparator.JSONComparator; 038 039import org.springframework.core.io.Resource; 040import org.springframework.util.ObjectUtils; 041import org.springframework.util.StringUtils; 042 043/** 044 * AssertJ {@link Assert} for {@link JsonContent}. 045 * 046 * @author Phillip Webb 047 * @author Andy Wilkinson 048 * @since 1.4.0 049 */ 050public class JsonContentAssert extends AbstractAssert<JsonContentAssert, CharSequence> { 051 052 private final JsonLoader loader; 053 054 /** 055 * Create a new {@link JsonContentAssert} instance that will load resources as UTF-8. 056 * @param resourceLoadClass the source class used to load resources 057 * @param json the actual JSON content 058 */ 059 public JsonContentAssert(Class<?> resourceLoadClass, CharSequence json) { 060 this(resourceLoadClass, null, json); 061 } 062 063 /** 064 * Create a new {@link JsonContentAssert} instance that will load resources in the 065 * given {@code charset}. 066 * @param resourceLoadClass the source class used to load resources 067 * @param charset the charset of the JSON resources 068 * @param json the actual JSON content 069 * @since 1.4.1 070 */ 071 public JsonContentAssert(Class<?> resourceLoadClass, Charset charset, 072 CharSequence json) { 073 super(json, JsonContentAssert.class); 074 this.loader = new JsonLoader(resourceLoadClass, charset); 075 } 076 077 /** 078 * Overridden version of {@code isEqualTo} to perform JSON tests based on the object 079 * type. 080 * @see org.assertj.core.api.AbstractAssert#isEqualTo(java.lang.Object) 081 */ 082 @Override 083 public JsonContentAssert isEqualTo(Object expected) { 084 if (expected == null || expected instanceof CharSequence) { 085 return isEqualToJson((CharSequence) expected); 086 } 087 if (expected instanceof byte[]) { 088 return isEqualToJson((byte[]) expected); 089 } 090 if (expected instanceof File) { 091 return isEqualToJson((File) expected); 092 } 093 if (expected instanceof InputStream) { 094 return isEqualToJson((InputStream) expected); 095 } 096 if (expected instanceof Resource) { 097 return isEqualToJson((Resource) expected); 098 } 099 throw new AssertionError("Unsupport type for JSON assert " + expected.getClass()); 100 } 101 102 /** 103 * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal 104 * to the specified JSON. The {@code expected} value can contain the JSON itself or, 105 * if it ends with {@code .json}, the name of a resource to be loaded using 106 * {@code resourceLoadClass}. 107 * @param expected the expected JSON or the name of a resource containing the expected 108 * JSON 109 * @return {@code this} assertion object 110 * @throws AssertionError if the actual JSON value is not equal to the given one 111 */ 112 public JsonContentAssert isEqualToJson(CharSequence expected) { 113 String expectedJson = this.loader.getJson(expected); 114 return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); 115 } 116 117 /** 118 * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal 119 * to the specified JSON resource. 120 * @param path the name of a resource containing the expected JSON 121 * @param resourceLoadClass the source class used to load the resource 122 * @return {@code this} assertion object 123 * @throws AssertionError if the actual JSON value is not equal to the given one 124 */ 125 public JsonContentAssert isEqualToJson(String path, Class<?> resourceLoadClass) { 126 String expectedJson = this.loader.getJson(path, resourceLoadClass); 127 return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); 128 } 129 130 /** 131 * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal 132 * to the specified JSON bytes. 133 * @param expected the expected JSON bytes 134 * @return {@code this} assertion object 135 * @throws AssertionError if the actual JSON value is not equal to the given one 136 */ 137 public JsonContentAssert isEqualToJson(byte[] expected) { 138 String expectedJson = this.loader.getJson(expected); 139 return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); 140 } 141 142 /** 143 * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal 144 * to the specified JSON file. 145 * @param expected a file containing the expected JSON 146 * @return {@code this} assertion object 147 * @throws AssertionError if the actual JSON value is not equal to the given one 148 */ 149 public JsonContentAssert isEqualToJson(File expected) { 150 String expectedJson = this.loader.getJson(expected); 151 return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); 152 } 153 154 /** 155 * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal 156 * to the specified JSON input stream. 157 * @param expected an input stream containing the expected JSON 158 * @return {@code this} assertion object 159 * @throws AssertionError if the actual JSON value is not equal to the given one 160 */ 161 public JsonContentAssert isEqualToJson(InputStream expected) { 162 String expectedJson = this.loader.getJson(expected); 163 return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); 164 } 165 166 /** 167 * Verifies that the actual value is {@link JSONCompareMode#LENIENT leniently} equal 168 * to the specified JSON resource. 169 * @param expected a resource containing the expected JSON 170 * @return {@code this} assertion object 171 * @throws AssertionError if the actual JSON value is not equal to the given one 172 */ 173 public JsonContentAssert isEqualToJson(Resource expected) { 174 String expectedJson = this.loader.getJson(expected); 175 return assertNotFailed(compare(expectedJson, JSONCompareMode.LENIENT)); 176 } 177 178 /** 179 * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to 180 * the specified JSON. The {@code expected} value can contain the JSON itself or, if 181 * it ends with {@code .json}, the name of a resource to be loaded using 182 * {@code resourceLoadClass}. 183 * @param expected the expected JSON or the name of a resource containing the expected 184 * JSON 185 * @return {@code this} assertion object 186 * @throws AssertionError if the actual JSON value is not equal to the given one 187 */ 188 public JsonContentAssert isStrictlyEqualToJson(CharSequence expected) { 189 String expectedJson = this.loader.getJson(expected); 190 return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); 191 } 192 193 /** 194 * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to 195 * the specified JSON resource. 196 * @param path the name of a resource containing the expected JSON 197 * @param resourceLoadClass the source class used to load the resource 198 * @return {@code this} assertion object 199 * @throws AssertionError if the actual JSON value is not equal to the given one 200 */ 201 public JsonContentAssert isStrictlyEqualToJson(String path, 202 Class<?> resourceLoadClass) { 203 String expectedJson = this.loader.getJson(path, resourceLoadClass); 204 return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); 205 } 206 207 /** 208 * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to 209 * the specified JSON bytes. 210 * @param expected the expected JSON bytes 211 * @return {@code this} assertion object 212 * @throws AssertionError if the actual JSON value is not equal to the given one 213 */ 214 public JsonContentAssert isStrictlyEqualToJson(byte[] expected) { 215 return assertNotFailed( 216 compare(this.loader.getJson(expected), JSONCompareMode.STRICT)); 217 } 218 219 /** 220 * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to 221 * the specified JSON file. 222 * @param expected a file containing the expected JSON 223 * @return {@code this} assertion object 224 * @throws AssertionError if the actual JSON value is not equal to the given one 225 */ 226 public JsonContentAssert isStrictlyEqualToJson(File expected) { 227 String expectedJson = this.loader.getJson(expected); 228 return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); 229 } 230 231 /** 232 * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to 233 * the specified JSON input stream. 234 * @param expected an input stream containing the expected JSON 235 * @return {@code this} assertion object 236 * @throws AssertionError if the actual JSON value is not equal to the given one 237 */ 238 public JsonContentAssert isStrictlyEqualToJson(InputStream expected) { 239 String expectedJson = this.loader.getJson(expected); 240 return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); 241 } 242 243 /** 244 * Verifies that the actual value is {@link JSONCompareMode#STRICT strictly} equal to 245 * the specified JSON resource. 246 * @param expected a resource containing the expected JSON 247 * @return {@code this} assertion object 248 * @throws AssertionError if the actual JSON value is not equal to the given one 249 */ 250 public JsonContentAssert isStrictlyEqualToJson(Resource expected) { 251 String expectedJson = this.loader.getJson(expected); 252 return assertNotFailed(compare(expectedJson, JSONCompareMode.STRICT)); 253 } 254 255 /** 256 * Verifies that the actual value is equal to the specified JSON. The {@code expected} 257 * value can contain the JSON itself or, if it ends with {@code .json}, the name of a 258 * resource to be loaded using {@code resourceLoadClass}. 259 * @param expected the expected JSON or the name of a resource containing the expected 260 * JSON 261 * @param compareMode the compare mode used when checking 262 * @return {@code this} assertion object 263 * @throws AssertionError if the actual JSON value is not equal to the given one 264 */ 265 public JsonContentAssert isEqualToJson(CharSequence expected, 266 JSONCompareMode compareMode) { 267 String expectedJson = this.loader.getJson(expected); 268 return assertNotFailed(compare(expectedJson, compareMode)); 269 } 270 271 /** 272 * Verifies that the actual value is equal to the specified JSON resource. 273 * @param path the name of a resource containing the expected JSON 274 * @param resourceLoadClass the source class used to load the resource 275 * @param compareMode the compare mode used when checking 276 * @return {@code this} assertion object 277 * @throws AssertionError if the actual JSON value is not equal to the given one 278 */ 279 public JsonContentAssert isEqualToJson(String path, Class<?> resourceLoadClass, 280 JSONCompareMode compareMode) { 281 String expectedJson = this.loader.getJson(path, resourceLoadClass); 282 return assertNotFailed(compare(expectedJson, compareMode)); 283 } 284 285 /** 286 * Verifies that the actual value is equal to the specified JSON bytes. 287 * @param expected the expected JSON bytes 288 * @param compareMode the compare mode used when checking 289 * @return {@code this} assertion object 290 * @throws AssertionError if the actual JSON value is not equal to the given one 291 */ 292 public JsonContentAssert isEqualToJson(byte[] expected, JSONCompareMode compareMode) { 293 String expectedJson = this.loader.getJson(expected); 294 return assertNotFailed(compare(expectedJson, compareMode)); 295 } 296 297 /** 298 * Verifies that the actual value is equal to the specified JSON file. 299 * @param expected a file containing the expected JSON 300 * @param compareMode the compare mode used when checking 301 * @return {@code this} assertion object 302 * @throws AssertionError if the actual JSON value is not equal to the given one 303 */ 304 public JsonContentAssert isEqualToJson(File expected, JSONCompareMode compareMode) { 305 String expectedJson = this.loader.getJson(expected); 306 return assertNotFailed(compare(expectedJson, compareMode)); 307 } 308 309 /** 310 * Verifies that the actual value is equal to the specified JSON input stream. 311 * @param expected an input stream containing the expected JSON 312 * @param compareMode the compare mode used when checking 313 * @return {@code this} assertion object 314 * @throws AssertionError if the actual JSON value is not equal to the given one 315 */ 316 public JsonContentAssert isEqualToJson(InputStream expected, 317 JSONCompareMode compareMode) { 318 return assertNotFailed(compare(this.loader.getJson(expected), compareMode)); 319 } 320 321 /** 322 * Verifies that the actual value is equal to the specified JSON resource. 323 * @param expected a resource containing the expected JSON 324 * @param compareMode the compare mode used when checking 325 * @return {@code this} assertion object 326 * @throws AssertionError if the actual JSON value is not equal to the given one 327 */ 328 public JsonContentAssert isEqualToJson(Resource expected, 329 JSONCompareMode compareMode) { 330 String expectedJson = this.loader.getJson(expected); 331 return assertNotFailed(compare(expectedJson, compareMode)); 332 } 333 334 /** 335 * Verifies that the actual value is equal to the specified JSON. The {@code expected} 336 * value can contain the JSON itself or, if it ends with {@code .json}, the name of a 337 * resource to be loaded using {@code resourceLoadClass}. 338 * @param expected the expected JSON or the name of a resource containing the expected 339 * JSON 340 * @param comparator the comparator used when checking 341 * @return {@code this} assertion object 342 * @throws AssertionError if the actual JSON value is not equal to the given one 343 */ 344 public JsonContentAssert isEqualToJson(CharSequence expected, 345 JSONComparator comparator) { 346 String expectedJson = this.loader.getJson(expected); 347 return assertNotFailed(compare(expectedJson, comparator)); 348 } 349 350 /** 351 * Verifies that the actual value is equal to the specified JSON resource. 352 * @param path the name of a resource containing the expected JSON 353 * @param resourceLoadClass the source class used to load the resource 354 * @param comparator the comparator used when checking 355 * @return {@code this} assertion object 356 * @throws AssertionError if the actual JSON value is not equal to the given one 357 */ 358 public JsonContentAssert isEqualToJson(String path, Class<?> resourceLoadClass, 359 JSONComparator comparator) { 360 String expectedJson = this.loader.getJson(path, resourceLoadClass); 361 return assertNotFailed(compare(expectedJson, comparator)); 362 } 363 364 /** 365 * Verifies that the actual value is equal to the specified JSON bytes. 366 * @param expected the expected JSON bytes 367 * @param comparator the comparator used when checking 368 * @return {@code this} assertion object 369 * @throws AssertionError if the actual JSON value is not equal to the given one 370 */ 371 public JsonContentAssert isEqualToJson(byte[] expected, JSONComparator comparator) { 372 String expectedJson = this.loader.getJson(expected); 373 return assertNotFailed(compare(expectedJson, comparator)); 374 } 375 376 /** 377 * Verifies that the actual value is equal to the specified JSON file. 378 * @param expected a file containing the expected JSON 379 * @param comparator the comparator used when checking 380 * @return {@code this} assertion object 381 * @throws AssertionError if the actual JSON value is not equal to the given one 382 */ 383 public JsonContentAssert isEqualToJson(File expected, JSONComparator comparator) { 384 String expectedJson = this.loader.getJson(expected); 385 return assertNotFailed(compare(expectedJson, comparator)); 386 } 387 388 /** 389 * Verifies that the actual value is equal to the specified JSON input stream. 390 * @param expected an input stream containing the expected JSON 391 * @param comparator the comparator used when checking 392 * @return {@code this} assertion object 393 * @throws AssertionError if the actual JSON value is not equal to the given one 394 */ 395 public JsonContentAssert isEqualToJson(InputStream expected, 396 JSONComparator comparator) { 397 String expectedJson = this.loader.getJson(expected); 398 return assertNotFailed(compare(expectedJson, comparator)); 399 } 400 401 /** 402 * Verifies that the actual value is equal to the specified JSON resource. 403 * @param expected a resource containing the expected JSON 404 * @param comparator the comparator used when checking 405 * @return {@code this} assertion object 406 * @throws AssertionError if the actual JSON value is not equal to the given one 407 */ 408 public JsonContentAssert isEqualToJson(Resource expected, JSONComparator comparator) { 409 String expectedJson = this.loader.getJson(expected); 410 return assertNotFailed(compare(expectedJson, comparator)); 411 } 412 413 /** 414 * Overridden version of {@code isNotEqualTo} to perform JSON tests based on the 415 * object type. 416 * @see org.assertj.core.api.AbstractAssert#isEqualTo(java.lang.Object) 417 */ 418 @Override 419 public JsonContentAssert isNotEqualTo(Object expected) { 420 if (expected == null || expected instanceof CharSequence) { 421 return isNotEqualToJson((CharSequence) expected); 422 } 423 if (expected instanceof byte[]) { 424 return isNotEqualToJson((byte[]) expected); 425 } 426 if (expected instanceof File) { 427 return isNotEqualToJson((File) expected); 428 } 429 if (expected instanceof InputStream) { 430 return isNotEqualToJson((InputStream) expected); 431 } 432 if (expected instanceof Resource) { 433 return isNotEqualToJson((Resource) expected); 434 } 435 throw new AssertionError("Unsupport type for JSON assert " + expected.getClass()); 436 } 437 438 /** 439 * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} 440 * equal to the specified JSON. The {@code expected} value can contain the JSON itself 441 * or, if it ends with {@code .json}, the name of a resource to be loaded using 442 * {@code resourceLoadClass}. 443 * @param expected the expected JSON or the name of a resource containing the expected 444 * JSON 445 * @return {@code this} assertion object 446 * @throws AssertionError if the actual JSON value is equal to the given one 447 */ 448 public JsonContentAssert isNotEqualToJson(CharSequence expected) { 449 String expectedJson = this.loader.getJson(expected); 450 return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); 451 } 452 453 /** 454 * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} 455 * equal to the specified JSON resource. 456 * @param path the name of a resource containing the expected JSON 457 * @param resourceLoadClass the source class used to load the resource 458 * @return {@code this} assertion object 459 * @throws AssertionError if the actual JSON value is equal to the given one 460 */ 461 public JsonContentAssert isNotEqualToJson(String path, Class<?> resourceLoadClass) { 462 String expectedJson = this.loader.getJson(path, resourceLoadClass); 463 return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); 464 } 465 466 /** 467 * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} 468 * equal to the specified JSON bytes. 469 * @param expected the expected JSON bytes 470 * @return {@code this} assertion object 471 * @throws AssertionError if the actual JSON value is equal to the given one 472 */ 473 public JsonContentAssert isNotEqualToJson(byte[] expected) { 474 String expectedJson = this.loader.getJson(expected); 475 return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); 476 } 477 478 /** 479 * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} 480 * equal to the specified JSON file. 481 * @param expected a file containing the expected JSON 482 * @return {@code this} assertion object 483 * @throws AssertionError if the actual JSON value is equal to the given one 484 */ 485 public JsonContentAssert isNotEqualToJson(File expected) { 486 String expectedJson = this.loader.getJson(expected); 487 return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); 488 } 489 490 /** 491 * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} 492 * equal to the specified JSON input stream. 493 * @param expected an input stream containing the expected JSON 494 * @return {@code this} assertion object 495 * @throws AssertionError if the actual JSON value is equal to the given one 496 */ 497 public JsonContentAssert isNotEqualToJson(InputStream expected) { 498 String expectedJson = this.loader.getJson(expected); 499 return assertNotPassed(compare(expectedJson, JSONCompareMode.LENIENT)); 500 } 501 502 /** 503 * Verifies that the actual value is not {@link JSONCompareMode#LENIENT leniently} 504 * equal to the specified JSON resource. 505 * @param expected a resource containing the expected JSON 506 * @return {@code this} assertion object 507 * @throws AssertionError if the actual JSON value is equal to the given one 508 */ 509 public JsonContentAssert isNotEqualToJson(Resource expected) { 510 return assertNotPassed( 511 compare(this.loader.getJson(expected), JSONCompareMode.LENIENT)); 512 } 513 514 /** 515 * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal 516 * to the specified JSON. The {@code expected} value can contain the JSON itself or, 517 * if it ends with {@code .json}, the name of a resource to be loaded using 518 * {@code resourceLoadClass}. 519 * @param expected the expected JSON or the name of a resource containing the expected 520 * JSON 521 * @return {@code this} assertion object 522 * @throws AssertionError if the actual JSON value is equal to the given one 523 */ 524 public JsonContentAssert isNotStrictlyEqualToJson(CharSequence expected) { 525 String expectedJson = this.loader.getJson(expected); 526 return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); 527 } 528 529 /** 530 * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal 531 * to the specified JSON resource. 532 * @param path the name of a resource containing the expected JSON 533 * @param resourceLoadClass the source class used to load the resource 534 * @return {@code this} assertion object 535 * @throws AssertionError if the actual JSON value is equal to the given one 536 */ 537 public JsonContentAssert isNotStrictlyEqualToJson(String path, 538 Class<?> resourceLoadClass) { 539 String expectedJson = this.loader.getJson(path, resourceLoadClass); 540 return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); 541 } 542 543 /** 544 * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal 545 * to the specified JSON bytes. 546 * @param expected the expected JSON bytes 547 * @return {@code this} assertion object 548 * @throws AssertionError if the actual JSON value is equal to the given one 549 */ 550 public JsonContentAssert isNotStrictlyEqualToJson(byte[] expected) { 551 String expectedJson = this.loader.getJson(expected); 552 return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); 553 } 554 555 /** 556 * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal 557 * to the specified JSON file. 558 * @param expected a file containing the expected JSON 559 * @return {@code this} assertion object 560 * @throws AssertionError if the actual JSON value is equal to the given one 561 */ 562 public JsonContentAssert isNotStrictlyEqualToJson(File expected) { 563 String expectedJson = this.loader.getJson(expected); 564 return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); 565 } 566 567 /** 568 * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal 569 * to the specified JSON input stream. 570 * @param expected an input stream containing the expected JSON 571 * @return {@code this} assertion object 572 * @throws AssertionError if the actual JSON value is equal to the given one 573 */ 574 public JsonContentAssert isNotStrictlyEqualToJson(InputStream expected) { 575 String expectedJson = this.loader.getJson(expected); 576 return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); 577 } 578 579 /** 580 * Verifies that the actual value is not {@link JSONCompareMode#STRICT strictly} equal 581 * to the specified JSON resource. 582 * @param expected a resource containing the expected JSON 583 * @return {@code this} assertion object 584 * @throws AssertionError if the actual JSON value is equal to the given one 585 */ 586 public JsonContentAssert isNotStrictlyEqualToJson(Resource expected) { 587 String expectedJson = this.loader.getJson(expected); 588 return assertNotPassed(compare(expectedJson, JSONCompareMode.STRICT)); 589 } 590 591 /** 592 * Verifies that the actual value is not equal to the specified JSON. The 593 * {@code expected} value can contain the JSON itself or, if it ends with 594 * {@code .json}, the name of a resource to be loaded using {@code resourceLoadClass}. 595 * @param expected the expected JSON or the name of a resource containing the expected 596 * JSON 597 * @param compareMode the compare mode used when checking 598 * @return {@code this} assertion object 599 * @throws AssertionError if the actual JSON value is equal to the given one 600 */ 601 public JsonContentAssert isNotEqualToJson(CharSequence expected, 602 JSONCompareMode compareMode) { 603 String expectedJson = this.loader.getJson(expected); 604 return assertNotPassed(compare(expectedJson, compareMode)); 605 } 606 607 /** 608 * Verifies that the actual value is not equal to the specified JSON resource. 609 * @param path the name of a resource containing the expected JSON 610 * @param resourceLoadClass the source class used to load the resource 611 * @param compareMode the compare mode used when checking 612 * @return {@code this} assertion object 613 * @throws AssertionError if the actual JSON value is equal to the given one 614 */ 615 public JsonContentAssert isNotEqualToJson(String path, Class<?> resourceLoadClass, 616 JSONCompareMode compareMode) { 617 String expectedJson = this.loader.getJson(path, resourceLoadClass); 618 return assertNotPassed(compare(expectedJson, compareMode)); 619 } 620 621 /** 622 * Verifies that the actual value is not equal to the specified JSON bytes. 623 * @param expected the expected JSON bytes 624 * @param compareMode the compare mode used when checking 625 * @return {@code this} assertion object 626 * @throws AssertionError if the actual JSON value is equal to the given one 627 */ 628 public JsonContentAssert isNotEqualToJson(byte[] expected, 629 JSONCompareMode compareMode) { 630 String expectedJson = this.loader.getJson(expected); 631 return assertNotPassed(compare(expectedJson, compareMode)); 632 } 633 634 /** 635 * Verifies that the actual value is not equal to the specified JSON file. 636 * @param expected a file containing the expected JSON 637 * @param compareMode the compare mode used when checking 638 * @return {@code this} assertion object 639 * @throws AssertionError if the actual JSON value is equal to the given one 640 */ 641 public JsonContentAssert isNotEqualToJson(File expected, 642 JSONCompareMode compareMode) { 643 String expectedJson = this.loader.getJson(expected); 644 return assertNotPassed(compare(expectedJson, compareMode)); 645 } 646 647 /** 648 * Verifies that the actual value is not equal to the specified JSON input stream. 649 * @param expected an input stream containing the expected JSON 650 * @param compareMode the compare mode used when checking 651 * @return {@code this} assertion object 652 * @throws AssertionError if the actual JSON value is equal to the given one 653 */ 654 public JsonContentAssert isNotEqualToJson(InputStream expected, 655 JSONCompareMode compareMode) { 656 String expectedJson = this.loader.getJson(expected); 657 return assertNotPassed(compare(expectedJson, compareMode)); 658 } 659 660 /** 661 * Verifies that the actual value is not equal to the specified JSON resource. 662 * @param expected a resource containing the expected JSON 663 * @param compareMode the compare mode used when checking 664 * @return {@code this} assertion object 665 * @throws AssertionError if the actual JSON value is equal to the given one 666 */ 667 public JsonContentAssert isNotEqualToJson(Resource expected, 668 JSONCompareMode compareMode) { 669 String expectedJson = this.loader.getJson(expected); 670 return assertNotPassed(compare(expectedJson, compareMode)); 671 } 672 673 /** 674 * Verifies that the actual value is not equal to the specified JSON. The 675 * {@code expected} value can contain the JSON itself or, if it ends with 676 * {@code .json}, the name of a resource to be loaded using {@code resourceLoadClass}. 677 * @param expected the expected JSON or the name of a resource containing the expected 678 * JSON 679 * @param comparator the comparator used when checking 680 * @return {@code this} assertion object 681 * @throws AssertionError if the actual JSON value is equal to the given one 682 */ 683 public JsonContentAssert isNotEqualToJson(CharSequence expected, 684 JSONComparator comparator) { 685 String expectedJson = this.loader.getJson(expected); 686 return assertNotPassed(compare(expectedJson, comparator)); 687 } 688 689 /** 690 * Verifies that the actual value is not equal to the specified JSON resource. 691 * @param path the name of a resource containing the expected JSON 692 * @param resourceLoadClass the source class used to load the resource 693 * @param comparator the comparator used when checking 694 * @return {@code this} assertion object 695 * @throws AssertionError if the actual JSON value is equal to the given one 696 */ 697 public JsonContentAssert isNotEqualToJson(String path, Class<?> resourceLoadClass, 698 JSONComparator comparator) { 699 String expectedJson = this.loader.getJson(path, resourceLoadClass); 700 return assertNotPassed(compare(expectedJson, comparator)); 701 } 702 703 /** 704 * Verifies that the actual value is not equal to the specified JSON bytes. 705 * @param expected the expected JSON bytes 706 * @param comparator the comparator used when checking 707 * @return {@code this} assertion object 708 * @throws AssertionError if the actual JSON value is equal to the given one 709 */ 710 public JsonContentAssert isNotEqualToJson(byte[] expected, 711 JSONComparator comparator) { 712 String expectedJson = this.loader.getJson(expected); 713 return assertNotPassed(compare(expectedJson, comparator)); 714 } 715 716 /** 717 * Verifies that the actual value is not equal to the specified JSON file. 718 * @param expected a file containing the expected JSON 719 * @param comparator the comparator used when checking 720 * @return {@code this} assertion object 721 * @throws AssertionError if the actual JSON value is equal to the given one 722 */ 723 public JsonContentAssert isNotEqualToJson(File expected, JSONComparator comparator) { 724 String expectedJson = this.loader.getJson(expected); 725 return assertNotPassed(compare(expectedJson, comparator)); 726 } 727 728 /** 729 * Verifies that the actual value is not equal to the specified JSON input stream. 730 * @param expected an input stream containing the expected JSON 731 * @param comparator the comparator used when checking 732 * @return {@code this} assertion object 733 * @throws AssertionError if the actual JSON value is equal to the given one 734 */ 735 public JsonContentAssert isNotEqualToJson(InputStream expected, 736 JSONComparator comparator) { 737 String expectedJson = this.loader.getJson(expected); 738 return assertNotPassed(compare(expectedJson, comparator)); 739 } 740 741 /** 742 * Verifies that the actual value is not equal to the specified JSON resource. 743 * @param expected a resource containing the expected JSON 744 * @param comparator the comparator used when checking 745 * @return {@code this} assertion object 746 * @throws AssertionError if the actual JSON value is equal to the given one 747 */ 748 public JsonContentAssert isNotEqualToJson(Resource expected, 749 JSONComparator comparator) { 750 String expectedJson = this.loader.getJson(expected); 751 return assertNotPassed(compare(expectedJson, comparator)); 752 } 753 754 /** 755 * Verify that the actual value at the given JSON path produces a non-null result. If 756 * the JSON path expression is not {@linkplain JsonPath#isDefinite() definite}, this 757 * method verifies that the value at the given path is not <em>empty</em>. 758 * @param expression the {@link JsonPath} expression 759 * @param args arguments to parameterize the {@code JsonPath} expression with, using 760 * formatting specifiers defined in {@link String#format(String, Object...)} 761 * @return {@code this} assertion object 762 * @throws AssertionError if the value at the given path is missing 763 */ 764 public JsonContentAssert hasJsonPathValue(CharSequence expression, Object... args) { 765 new JsonPathValue(expression, args).assertHasValue(Object.class, "an object"); 766 return this; 767 } 768 769 /** 770 * Verify that the actual value at the given JSON path produces a non-null string 771 * result. 772 * @param expression the {@link JsonPath} expression 773 * @param args arguments to parameterize the {@code JsonPath} expression with, using 774 * formatting specifiers defined in {@link String#format(String, Object...)} 775 * @return {@code this} assertion object 776 * @throws AssertionError if the value at the given path is missing or not a string 777 */ 778 public JsonContentAssert hasJsonPathStringValue(CharSequence expression, 779 Object... args) { 780 new JsonPathValue(expression, args).assertHasValue(String.class, "a string"); 781 return this; 782 } 783 784 /** 785 * Verify that the actual value at the given JSON path produces a non-null number 786 * result. 787 * @param expression the {@link JsonPath} expression 788 * @param args arguments to parameterize the {@code JsonPath} expression with, using 789 * formatting specifiers defined in {@link String#format(String, Object...)} 790 * @return {@code this} assertion object 791 * @throws AssertionError if the value at the given path is missing or not a number 792 */ 793 public JsonContentAssert hasJsonPathNumberValue(CharSequence expression, 794 Object... args) { 795 new JsonPathValue(expression, args).assertHasValue(Number.class, "a number"); 796 return this; 797 } 798 799 /** 800 * Verify that the actual value at the given JSON path produces a non-null boolean 801 * result. 802 * @param expression the {@link JsonPath} expression 803 * @param args arguments to parameterize the {@code JsonPath} expression with, using 804 * formatting specifiers defined in {@link String#format(String, Object...)} 805 * @return {@code this} assertion object 806 * @throws AssertionError if the value at the given path is missing or not a boolean 807 */ 808 public JsonContentAssert hasJsonPathBooleanValue(CharSequence expression, 809 Object... args) { 810 new JsonPathValue(expression, args).assertHasValue(Boolean.class, "a boolean"); 811 return this; 812 } 813 814 /** 815 * Verify that the actual value at the given JSON path produces a non-null array 816 * result. 817 * @param expression the {@link JsonPath} expression 818 * @param args arguments to parameterize the {@code JsonPath} expression with, using 819 * formatting specifiers defined in {@link String#format(String, Object...)} 820 * @return {@code this} assertion object 821 * @throws AssertionError if the value at the given path is missing or not an array 822 */ 823 public JsonContentAssert hasJsonPathArrayValue(CharSequence expression, 824 Object... args) { 825 new JsonPathValue(expression, args).assertHasValue(List.class, "an array"); 826 return this; 827 } 828 829 /** 830 * Verify that the actual value at the given JSON path produces a non-null map result. 831 * @param expression the {@link JsonPath} expression 832 * @param args arguments to parameterize the {@code JsonPath} expression with, using 833 * formatting specifiers defined in {@link String#format(String, Object...)} 834 * @return {@code this} assertion object 835 * @throws AssertionError if the value at the given path is missing or not a map 836 */ 837 public JsonContentAssert hasJsonPathMapValue(CharSequence expression, 838 Object... args) { 839 new JsonPathValue(expression, args).assertHasValue(Map.class, "a map"); 840 return this; 841 } 842 843 /** 844 * Verify that the actual value at the given JSON path produces an 845 * {@link ObjectUtils#isEmpty(Object) empty} result. 846 * @param expression the {@link JsonPath} expression 847 * @param args arguments to parameterize the {@code JsonPath} expression with, using 848 * formatting specifiers defined in {@link String#format(String, Object...)} 849 * @return {@code this} assertion object 850 * @throws AssertionError if the value at the given path is not empty 851 */ 852 public JsonContentAssert hasEmptyJsonPathValue(CharSequence expression, 853 Object... args) { 854 new JsonPathValue(expression, args).assertHasEmptyValue(); 855 return this; 856 } 857 858 /** 859 * Verify that the actual value at the given JSON path produces no result. If the JSON 860 * path expression is not {@linkplain JsonPath#isDefinite() definite}, this method 861 * verifies that the value at the given path is <em>empty</em>. 862 * @param expression the {@link JsonPath} expression 863 * @param args arguments to parameterize the {@code JsonPath} expression with, using 864 * formatting specifiers defined in {@link String#format(String, Object...)} 865 * @return {@code this} assertion object 866 * @throws AssertionError if the value at the given path is not missing 867 */ 868 public JsonContentAssert doesNotHaveJsonPathValue(CharSequence expression, 869 Object... args) { 870 new JsonPathValue(expression, args).assertDoesNotHaveValue(); 871 return this; 872 } 873 874 /** 875 * Verify that the actual value at the given JSON path does not produce an 876 * {@link ObjectUtils#isEmpty(Object) empty} result. 877 * @param expression the {@link JsonPath} expression 878 * @param args arguments to parameterize the {@code JsonPath} expression with, using 879 * formatting specifiers defined in {@link String#format(String, Object...)} 880 * @return {@code this} assertion object 881 * @throws AssertionError if the value at the given path is empty 882 */ 883 public JsonContentAssert doesNotHaveEmptyJsonPathValue(CharSequence expression, 884 Object... args) { 885 new JsonPathValue(expression, args).assertDoesNotHaveEmptyValue(); 886 return this; 887 } 888 889 /** 890 * Extract the value at the given JSON path for further object assertions. 891 * @param expression the {@link JsonPath} expression 892 * @param args arguments to parameterize the {@code JsonPath} expression with, using 893 * formatting specifiers defined in {@link String#format(String, Object...)} 894 * @return a new assertion object whose object under test is the extracted item 895 * @throws AssertionError if the path is not valid 896 */ 897 public AbstractObjectAssert<?, Object> extractingJsonPathValue( 898 CharSequence expression, Object... args) { 899 return Assertions.assertThat(new JsonPathValue(expression, args).getValue(false)); 900 } 901 902 /** 903 * Extract the string value at the given JSON path for further object assertions. 904 * @param expression the {@link JsonPath} expression 905 * @param args arguments to parameterize the {@code JsonPath} expression with, using 906 * formatting specifiers defined in {@link String#format(String, Object...)} 907 * @return a new assertion object whose object under test is the extracted item 908 * @throws AssertionError if the path is not valid or does not result in a string 909 */ 910 public AbstractCharSequenceAssert<?, String> extractingJsonPathStringValue( 911 CharSequence expression, Object... args) { 912 return Assertions.assertThat( 913 extractingJsonPathValue(expression, args, String.class, "a string")); 914 } 915 916 /** 917 * Extract the number value at the given JSON path for further object assertions. 918 * @param expression the {@link JsonPath} expression 919 * @param args arguments to parameterize the {@code JsonPath} expression with, using 920 * formatting specifiers defined in {@link String#format(String, Object...)} 921 * @return a new assertion object whose object under test is the extracted item 922 * @throws AssertionError if the path is not valid or does not result in a number 923 */ 924 public AbstractObjectAssert<?, Number> extractingJsonPathNumberValue( 925 CharSequence expression, Object... args) { 926 return Assertions.assertThat( 927 extractingJsonPathValue(expression, args, Number.class, "a number")); 928 } 929 930 /** 931 * Extract the boolean value at the given JSON path for further object assertions. 932 * @param expression the {@link JsonPath} expression 933 * @param args arguments to parameterize the {@code JsonPath} expression with, using 934 * formatting specifiers defined in {@link String#format(String, Object...)} 935 * @return a new assertion object whose object under test is the extracted item 936 * @throws AssertionError if the path is not valid or does not result in a boolean 937 */ 938 public AbstractBooleanAssert<?> extractingJsonPathBooleanValue( 939 CharSequence expression, Object... args) { 940 return Assertions.assertThat( 941 extractingJsonPathValue(expression, args, Boolean.class, "a boolean")); 942 } 943 944 /** 945 * Extract the array value at the given JSON path for further object assertions. 946 * @param expression the {@link JsonPath} expression 947 * @param args arguments to parameterize the {@code JsonPath} expression with, using 948 * formatting specifiers defined in {@link String#format(String, Object...)} 949 * @param <E> element type 950 * @return a new assertion object whose object under test is the extracted item 951 * @throws AssertionError if the path is not valid or does not result in an array 952 */ 953 @SuppressWarnings("unchecked") 954 public <E> ListAssert<E> extractingJsonPathArrayValue(CharSequence expression, 955 Object... args) { 956 return Assertions.assertThat( 957 extractingJsonPathValue(expression, args, List.class, "an array")); 958 } 959 960 /** 961 * Extract the map value at the given JSON path for further object assertions. 962 * @param expression the {@link JsonPath} expression 963 * @param args arguments to parameterize the {@code JsonPath} expression with, using 964 * formatting specifiers defined in {@link String#format(String, Object...)} 965 * @param <K> key type 966 * @param <V> value type 967 * @return a new assertion object whose object under test is the extracted item 968 * @throws AssertionError if the path is not valid or does not result in a map 969 */ 970 @SuppressWarnings("unchecked") 971 public <K, V> MapAssert<K, V> extractingJsonPathMapValue(CharSequence expression, 972 Object... args) { 973 return Assertions.assertThat( 974 extractingJsonPathValue(expression, args, Map.class, "a map")); 975 } 976 977 @SuppressWarnings("unchecked") 978 private <T> T extractingJsonPathValue(CharSequence expression, Object[] args, 979 Class<T> type, String expectedDescription) { 980 JsonPathValue value = new JsonPathValue(expression, args); 981 if (value.getValue(false) != null) { 982 value.assertHasValue(type, expectedDescription); 983 } 984 return (T) value.getValue(false); 985 } 986 987 private JSONCompareResult compare(CharSequence expectedJson, 988 JSONCompareMode compareMode) { 989 if (this.actual == null) { 990 return compareForNull(expectedJson); 991 } 992 try { 993 return JSONCompare.compareJSON( 994 (expectedJson != null) ? expectedJson.toString() : null, 995 this.actual.toString(), compareMode); 996 } 997 catch (Exception ex) { 998 if (ex instanceof RuntimeException) { 999 throw (RuntimeException) ex; 1000 } 1001 throw new IllegalStateException(ex); 1002 } 1003 } 1004 1005 private JSONCompareResult compare(CharSequence expectedJson, 1006 JSONComparator comparator) { 1007 if (this.actual == null) { 1008 return compareForNull(expectedJson); 1009 } 1010 try { 1011 return JSONCompare.compareJSON( 1012 (expectedJson != null) ? expectedJson.toString() : null, 1013 this.actual.toString(), comparator); 1014 } 1015 catch (Exception ex) { 1016 if (ex instanceof RuntimeException) { 1017 throw (RuntimeException) ex; 1018 } 1019 throw new IllegalStateException(ex); 1020 } 1021 } 1022 1023 private JSONCompareResult compareForNull(CharSequence expectedJson) { 1024 JSONCompareResult result = new JSONCompareResult(); 1025 result.passed(); 1026 if (expectedJson != null) { 1027 result.fail("Expected null JSON"); 1028 } 1029 return result; 1030 } 1031 1032 private JsonContentAssert assertNotFailed(JSONCompareResult result) { 1033 if (result.failed()) { 1034 throw new AssertionError("JSON Comparison failure: " + result.getMessage()); 1035 } 1036 return this; 1037 } 1038 1039 private JsonContentAssert assertNotPassed(JSONCompareResult result) { 1040 if (result.passed()) { 1041 throw new AssertionError("JSON Comparison failure: " + result.getMessage()); 1042 } 1043 return this; 1044 } 1045 1046 /** 1047 * A {@link JsonPath} value. 1048 */ 1049 private class JsonPathValue { 1050 1051 private final String expression; 1052 1053 private final JsonPath jsonPath; 1054 1055 JsonPathValue(CharSequence expression, Object... args) { 1056 org.springframework.util.Assert.hasText( 1057 (expression != null) ? expression.toString() : null, 1058 "expression must not be null or empty"); 1059 this.expression = String.format(expression.toString(), args); 1060 this.jsonPath = JsonPath.compile(this.expression); 1061 } 1062 1063 public void assertHasEmptyValue() { 1064 if (ObjectUtils.isEmpty(getValue(false)) || isIndefiniteAndEmpty()) { 1065 return; 1066 } 1067 throw new AssertionError(getExpectedValueMessage("an empty value")); 1068 } 1069 1070 public void assertDoesNotHaveEmptyValue() { 1071 if (!ObjectUtils.isEmpty(getValue(false))) { 1072 return; 1073 } 1074 throw new AssertionError(getExpectedValueMessage("a non-empty value")); 1075 1076 } 1077 1078 public void assertHasValue(Class<?> type, String expectedDescription) { 1079 Object value = getValue(true); 1080 if (value == null || isIndefiniteAndEmpty()) { 1081 throw new AssertionError(getNoValueMessage()); 1082 } 1083 if (type != null && !type.isInstance(value)) { 1084 throw new AssertionError(getExpectedValueMessage(expectedDescription)); 1085 } 1086 } 1087 1088 public void assertDoesNotHaveValue() { 1089 if (getValue(false) == null || isIndefiniteAndEmpty()) { 1090 return; 1091 } 1092 throw new AssertionError(getExpectedValueMessage("no value")); 1093 } 1094 1095 private boolean isIndefiniteAndEmpty() { 1096 return !isDefinite() && isEmpty(); 1097 } 1098 1099 private boolean isDefinite() { 1100 return this.jsonPath.isDefinite(); 1101 } 1102 1103 private boolean isEmpty() { 1104 return ObjectUtils.isEmpty(getValue(false)); 1105 } 1106 1107 public Object getValue(boolean required) { 1108 try { 1109 CharSequence json = JsonContentAssert.this.actual; 1110 return this.jsonPath.read((json != null) ? json.toString() : null); 1111 } 1112 catch (Exception ex) { 1113 if (!required) { 1114 return null; 1115 } 1116 throw new AssertionError(getNoValueMessage() + ". " + ex.getMessage()); 1117 } 1118 } 1119 1120 private String getNoValueMessage() { 1121 return "No value at JSON path \"" + this.expression + "\""; 1122 } 1123 1124 private String getExpectedValueMessage(String expectedDescription) { 1125 return String.format("Expected %s at JSON path \"%s\" but found: %s", 1126 expectedDescription, this.expression, ObjectUtils.nullSafeToString( 1127 StringUtils.quoteIfString(getValue(false)))); 1128 } 1129 1130 } 1131 1132}