diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml new file mode 100644 index 00000000..fb527d76 --- /dev/null +++ b/.github/workflows/deploy-website.yml @@ -0,0 +1,50 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch - disabled, manual only + #push: + # branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Run the Maven javadoc command + run: mvn javadoc:aggregate -DreportOutputDirectory=src/site/resources/apidocs + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: './src/site/resources' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ec376bb8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +target \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9421e216..1b33ee64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,64 @@ Changelog ========= +Version 2.0-rc1 - 7/28/2024 +------------------- + - Release candidate + - ** Switches JSON implementation to use org.json:json:20240303 ** + - Deployment still built with Java version 8 to maximize compatibility + - Cannot insert null directly into JSONArray without casting. Recommend to use JSONObject.Null + - JSONException is now a RuntimeException. Is not defined as thrown in method signatures anynmore. + +Version 1.5.3 - 6/28/2024 +------------------------- + - Revert Java release version from 21 to 8 due to breaking older compilers. + +Version 1.5.2 - 6/14/2024 +------------------------- + - Fix CVE-2020-15250 JUnit vulnerability (https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15250). Bump + dependencies. + - Add gitIgnore file + - README syntax error fix + - Accidentally upgraded release to Java version 21 + +Version 1.5.1 - 7/4/2022 +------------------------ +Going to try to catch up on some ancient PRs, mainly around security and cleanup. Starting with accepted PRs that +didn't get released yet. To be followed hopefully shortly with another release. + - Added convenience methods for JSONObject comparison using a custom JSONComparator (thanks jakob-o@!) + - Fix issue #105: Issue when comparing JSONArray if any value is null (thanks suraj1291993@!) + - Fixes security vulnerability associated with older version of junit + +Version 1.5.0 - 3/19/2017 +------------------------- + - JSONassert now supports user-supplied error messages (thanks yasin3061@!) + - Some refactoring / code health cleanup (thanks picimako@!) + - License headers on individual files + - Java 8 friendly javadocs + +Version 1.4.0 - 10/30/2016 +-------------------------- + - Change the implementation for org.json to one with a more open license + - Fix null pointer exception (issue #48) + - Support wildcards in Customization.path + +Version 1.3.0 - 12/16/2015 +-------------------------- + - Fix & improve ArrayValueMatcher JavaDoc (thanks dmackinder@!) + Fix final JavaDoc example and add new example showing how to verify + every array element using a custom comparator + - Fix URL in pom.xml (aukevanleeuwen@) + - Update JSONCompareResult.java adding 2 new lists for missing and unexpected fileds (thanks riccorazza@!) + - Includes missing imports in test class (thanks javierseixas@!) + +Version 1.2.3 - 2/5/2014 +------------------------ + - This edition brought to you by dmackinder (thanks dmackinder!) + - Added array size comparator enhancements. + - Added ArrayValueMatcher to simplify verification of range of array elements. + - Improve diagnostics from RegularExpressionValueMatcher. + - Deprecated former Customization.matches() signature + Version 1.2.2 - 12/31/2013 -------------------------- - Add support for JSONString @@ -8,7 +66,8 @@ Version 1.2.2 - 12/31/2013 Version 1.2.1 - 10/24/2013 -------------------------- - Remove commons-collection dependency - - Updated Customization class to allow path-matching, and matching of expected and actual values with user-provided EqualityComparator. + - Updated Customization class to allow path-matching, and matching of expected and actual values with user-provided + EqualityComparator. - Added AssertNotEquals Version 1.2.0 - 3/17/2013 diff --git a/README.md b/README.md index 30a85064..871b1e19 100644 --- a/README.md +++ b/README.md @@ -29,22 +29,23 @@ Assert.assertTrue(data.has("friends")); Object friendsObject = data.get("friends"); Assert.assertTrue(friendsObject instanceof JSONArray); JSONArray friends = (JSONArray) friendsObject; -Assert.assertEquals(2, data.length()); -JSONObject friend1Obj = friends.getJSONObject(data.get(0)); -Assert.true(friend1Obj.has("id")); -Assert.true(friend1Obj.has("name")); -JSONObject friend2Obj = friends.getJSONObject(data.get(1)); -Assert.true(friend2Obj.has("id")); -Assert.true(friend2Obj.has("name")); +Assert.assertEquals(2, friends.length()); +JSONObject friend1Obj = friends.getJSONObject(0); +Assert.assertTrue(friend1Obj.has("id")); +Assert.assertTrue(friend1Obj.has("name")); +JSONObject friend2Obj = friends.getJSONObject(1); +Assert.assertTrue(friend2Obj.has("id")); +Assert.assertTrue(friend2Obj.has("name")); + if ("Carter Page".equals(friend1Obj.getString("name"))) { - Assert.assertEquals(123, friend1Obj.getInt("id")); + Assert.assertEquals(456, friend1Obj.getInt("id")); Assert.assertEquals("Corby Page", friend2Obj.getString("name")); - Assert.assertEquals(456, friend2Obj.getInt("id")); + Assert.assertEquals(123, friend2Obj.getInt("id")); } else if ("Corby Page".equals(friend1Obj.getString("name"))) { - Assert.assertEquals(456, friend1Obj.getInt("id")); + Assert.assertEquals(123, friend1Obj.getInt("id")); Assert.assertEquals("Carter Page", friend2Obj.getString("name")); - Assert.assertEquals(123, friend2Obj.getInt("id")); + Assert.assertEquals(456, friend2Obj.getInt("id")); } else { Assert.fail("Expected either Carter or Corby, Got: " + friend1Obj.getString("name")); @@ -76,8 +77,8 @@ To use, [download the JAR](https://github.com/skyscreamer/JSONassert/releases) o org.skyscreamer jsonassert - 1.2.2 - test + 2.0-rc1 + test Write tests like this: @@ -96,9 +97,16 @@ Who uses JSONassert? + [GroupDocs](http://groupdocs.com/) + [Shazam](http://www.shazam.com/) + [Thucydides](http://thucydides.net/) + + [and over a thousand more](https://mvnrepository.com/artifact/org.skyscreamer/jsonassert)... * * * +org.json +-------- + +As of v2, JSONAssert uses @stleary's [JSON-java](https://github.com/stleary/JSON-java) implementation of org.json, the +most commonly used reference implementation for JSON in Java. + Resources --------- diff --git a/pom.xml b/pom.xml index 13afc2ee..86738534 100644 --- a/pom.xml +++ b/pom.xml @@ -2,20 +2,18 @@ 4.0.0 - - org.sonatype.oss - oss-parent - 7 - - org.skyscreamer jsonassert - 1.2.3-SNAPSHOT + 2.0-rc1 jar JSONassert - A library to develop RESTful but flexible APIs - http://github.com/skyscreamer/yoga + Write JSON unit tests in less code. Great for testing REST interfaces. + https://github.com/skyscreamer/JSONassert + + + 8 + @@ -36,14 +34,8 @@ carter@skyscreamer.org - cepage - Corby Page - corby@skyscreamer.org - - - sduskis - Solomon Duskis - solomon@skyscreamer.org + hertzsprung + James Shaw @@ -52,12 +44,18 @@ org.json json - 20090211 + 20240303 junit junit - 4.10 + 4.13.2 + test + + + org.hamcrest + hamcrest + 2.2 test @@ -67,44 +65,27 @@ org.apache.maven.plugins maven-compiler-plugin - 2.3.1 - - 1.6 - 1.6 - + 3.11.0 - org.apache.maven.plugins - maven-site-plugin - 3.1 + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true - - - org.codehaus.mojo - cobertura-maven-plugin - 2.5.1 - - + ossrh + https://oss.sonatype.org/ + false + + + com.thoughtworks.xstream + xstream + 1.4.15 + + - - - org.apache.maven.scm - maven-scm-provider-gitexe - 1.3 - - - org.apache.maven.scm - maven-scm-manager-plexus - 1.3 - - - org.kathrynhuxtable.maven.wagon - wagon-gitsite - 0.3.1 - - @@ -112,23 +93,51 @@ github-project-site gitsite:git@github.com/skyscreamer/JSONassert.git + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + - release-sign-artifacts - - - performRelease - true - - + deploy + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + ${java.home}/bin/javadoc + ${maven.compiler.release} + + org.apache.maven.plugins maven-gpg-plugin - 1.1 + 1.5 sign-artifacts diff --git a/src/main/java/org/skyscreamer/jsonassert/ArrayValueMatcher.java b/src/main/java/org/skyscreamer/jsonassert/ArrayValueMatcher.java new file mode 100644 index 00000000..2d4dd8ec --- /dev/null +++ b/src/main/java/org/skyscreamer/jsonassert/ArrayValueMatcher.java @@ -0,0 +1,222 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.skyscreamer.jsonassert; + +import java.text.MessageFormat; + +import org.json.JSONArray; +import org.json.JSONException; +import org.skyscreamer.jsonassert.comparator.JSONComparator; + +/** + *

A value matcher for arrays. This operates like STRICT_ORDER array match, + * however if expected array has less elements than actual array the matching + * process loops through the expected array to get expected elements for the + * additional actual elements. In general the expected array will contain a + * single element which is matched against each actual array element in turn. + * This allows simple verification of constant array element components and + * coupled with RegularExpressionValueMatcher can be used to match specific + * array element components against a regular expression pattern. As a convenience to reduce syntactic complexity of expected string, if the + * expected object is not an array, a one element expected array is created + * containing whatever is provided as the expected value.

+ * + *

Some examples of typical usage idioms listed below.

+ * + *

Assuming JSON to be verified is held in String variable ARRAY_OF_JSONOBJECTS and contains:

+ * + *
{@code
+ * {a:[{background:white, id:1, type:row},
+ *     {background:grey,  id:2, type:row},
+ *     {background:white, id:3, type:row},
+ *     {background:grey,  id:4, type:row}]}
+ * }
+ * + *

then:

+ * + *

To verify that the 'id' attribute of first element of array 'a' is '1':

+ * + *
{@code
+ * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
+ * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0));
+ * JSONAssert.assertEquals("{a:[{id:1}]}", ARRAY_OF_JSONOBJECTS,
+ *     new CustomComparator(JSONCompareMode.LENIENT, customization));
+ * }
+ *
+ * 

To simplify complexity of expected JSON string, the value "a:[{id:1}]}" may be replaced by "a:{id:1}}"

+ * + *

To verify that the 'type' attribute of second and third elements of array 'a' is 'row':

+ * + *
{@code
+ * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
+ * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1, 2));
+ * JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS,
+ *     new CustomComparator(JSONCompareMode.LENIENT, customization));
+ * }
+ * 
+ * 

To verify that the 'type' attribute of every element of array 'a' is 'row':

+ * + *
{@code
+ * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
+ * Customization customization = new Customization("a", new ArrayValueMatcher(comparator));
+ * JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS,
+ *     new CustomComparator(JSONCompareMode.LENIENT, customization));
+ * }
+ * 
+ * 

To verify that the 'id' attribute of every element of array 'a' matches regular expression '\d+'. This requires a custom comparator to specify regular expression to be used to validate each array element, hence the array of Customization instances:

+ * + *
{@code
+ * // get length of array we will verify
+ * int aLength = ((JSONArray)((JSONObject)JSONParser.parseJSON(ARRAY_OF_JSONOBJECTS)).get("a")).length();
+ * // create array of customizations one for each array element
+ * RegularExpressionValueMatcher regExValueMatcher =
+ *     new RegularExpressionValueMatcher("\\d+");  // matches one or more digits
+ * Customization[] customizations = new Customization[aLength];
+ * for (int i=0; i regExArrayValueMatcher = new ArrayValueMatcher(regExComparator);
+ * Customization regExArrayValueCustomization = new Customization("a", regExArrayValueMatcher);
+ * CustomComparator regExCustomArrayValueComparator =
+ *     new CustomComparator(JSONCompareMode.STRICT_ORDER, new Customization[] { regExArrayValueCustomization });
+ * JSONAssert.assertEquals("{a:[{id:X}]}", ARRAY_OF_JSONOBJECTS, regExCustomArrayValueComparator);
+ * }
+ * 
+ * 

To verify that the 'background' attribute of every element of array 'a' alternates between 'white' and 'grey' starting with first element 'background' being 'white':

+ * + *
{@code
+ * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
+ * Customization customization = new Customization("a", new ArrayValueMatcher(comparator));
+ * JSONAssert.assertEquals("{a:[{background:white},{background:grey}]}", ARRAY_OF_JSONOBJECTS,
+ *     new CustomComparator(JSONCompareMode.LENIENT, customization));
+ * }
+ * 
+ * 

Assuming JSON to be verified is held in String variable ARRAY_OF_JSONARRAYS and contains:

+ * + * {a:[[6,7,8], [9,10,11], [12,13,14], [19,20,21,22]]} + * + *

then:

+ * + *

To verify that the first three elements of JSON array 'a' are JSON arrays of length 3:

+ * + *
{@code
+ * JSONComparator comparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER);
+ * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0, 2));
+ * JSONAssert.assertEquals("{a:[[3]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization));
+ * }
+ *
+ * 

NOTE: simplified expected JSON strings are not possible in this case as ArraySizeComparator does not support them.

+ * + *

To verify that the second elements of JSON array 'a' is a JSON array whose first element has the value 9:

+ * + *
{@code
+ * JSONComparator innerComparator = new DefaultComparator(JSONCompareMode.LENIENT);
+ * Customization innerCustomization = new Customization("a[1]", new ArrayValueMatcher(innerComparator, 0));
+ * JSONComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, innerCustomization);
+ * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1));
+ * JSONAssert.assertEquals("{a:[[9]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization));
+ * }
+ *
+ * 

To simplify complexity of expected JSON string, the value "{a:[[9]]}" may be replaced by "{a:[9]}" or "{a:9}"

+ * + * @author Duncan Mackinder + * @param Array Type + */ +public class ArrayValueMatcher implements LocationAwareValueMatcher { + private final JSONComparator comparator; + private final int from; + private final int to; + + /** + * Create ArrayValueMatcher to match every element in actual array against + * elements taken in sequence from expected array, repeating from start of + * expected array if necessary. + * + * @param comparator + * comparator to use to compare elements + */ + public ArrayValueMatcher(JSONComparator comparator) { + this(comparator, 0, Integer.MAX_VALUE); + } + + /** + * Create ArrayValueMatcher to match specified element in actual array + * against first element of expected array. + * + * @param comparator + * comparator to use to compare elements + * @param index + * index of the array element to be compared + */ + public ArrayValueMatcher(JSONComparator comparator, int index) { + this(comparator, index, index); + } + + /** + * Create ArrayValueMatcher to match every element in specified range + * (inclusive) from actual array against elements taken in sequence from + * expected array, repeating from start of expected array if necessary. + * + * @param comparator + * comparator to use to compare elements + * @param from first element in actual array to compared + * @param to last element in actual array to compared + */ + public ArrayValueMatcher(JSONComparator comparator, int from, int to) { + assert comparator != null : "comparator null"; + assert from >= 0 : MessageFormat.format("from({0}) < 0", from); + assert to >= from : MessageFormat.format("to({0}) < from({1})", to, + from); + this.comparator = comparator; + this.from = from; + this.to = to; + } + + @Override + /* + * NOTE: method defined as required by ValueMatcher interface but will never + * be called so defined simply to indicate match failure + */ + public boolean equal(T o1, T o2) { + return false; + } + + @Override + public boolean equal(String prefix, T actual, T expected, JSONCompareResult result) { + if (!(actual instanceof JSONArray)) { + throw new IllegalArgumentException("ArrayValueMatcher applied to non-array actual value"); + } + try { + JSONArray actualArray = (JSONArray) actual; + JSONArray expectedArray = expected instanceof JSONArray ? (JSONArray) expected: new JSONArray(new Object[] { expected }); + int first = Math.max(0, from); + int last = Math.min(actualArray.length() - 1, to); + int expectedLen = expectedArray.length(); + for (int i = first; i <= last; i++) { + String elementPrefix = MessageFormat.format("{0}[{1}]", prefix, i); + Object actualElement = actualArray.get(i); + Object expectedElement = expectedArray.get((i - first) % expectedLen); + comparator.compareValues(elementPrefix, expectedElement, actualElement, result); + } + // any failures have already been passed to result, so return true + return true; + } + catch (JSONException e) { + return false; + } + } + +} diff --git a/src/main/java/org/skyscreamer/jsonassert/Customization.java b/src/main/java/org/skyscreamer/jsonassert/Customization.java index 71155a0b..c812a447 100644 --- a/src/main/java/org/skyscreamer/jsonassert/Customization.java +++ b/src/main/java/org/skyscreamer/jsonassert/Customization.java @@ -1,28 +1,151 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; +import java.util.regex.Pattern; + /** * Associates a custom matcher to a specific jsonpath. */ public final class Customization { - private final String path; + private final Pattern path; private final ValueMatcher comparator; public Customization(String path, ValueMatcher comparator) { assert path != null; assert comparator != null; - this.path = path; + this.path = Pattern.compile(buildPattern(path)); this.comparator = comparator; } + private String buildPattern(String path) { + return buildPatternLevel1(path); + } + + private String buildPatternLevel1(String path) { + String regex = "\\*\\*\\."; + String replacement = "(?:.+\\.)?"; + + return buildPattern(path, regex, replacement, 1); + } + + private String buildPatternLevel2(String s) { + if (s.isEmpty()) { + return ""; + } + String regex = "\\*\\*"; + String replacement = ".+"; + + return buildPattern(s, regex, replacement, 2); + } + + private String buildPatternLevel3(String s) { + if (s.isEmpty()) { + return ""; + } + + String regex = "\\*"; + String replacement = "[^\\.]+"; + + return buildPattern(s, regex, replacement, 3); + } + + private String buildPattern(String path, String regex, String replacement, int level) { + StringBuilder sb = new StringBuilder(); + String[] parts = path.split(regex); + for (int i = 0; i < parts.length; i++) { + sb.append(buildPatternForLevel(level, parts[i])); + if (i < parts.length - 1) { + sb.append(replacement); + } + } + return sb.toString(); + } + + private String buildPatternForLevel(int level, String part) { + switch (level) { + case 1: + return buildPatternLevel2(part); + case 2: + return buildPatternLevel3(part); + case 3: + return Pattern.quote(part); + default: + return "Incorrect level."; + } + } + + /** + * Creates a new {@link Customization} instance for {@code path} and {@code comparator}. + * + * @param path the json path + * @param comparator the comparator + * @return a new Customization + */ public static Customization customization(String path, ValueMatcher comparator) { return new Customization(path, comparator); } public boolean appliesToPath(String path) { - return this.path.equals(path); + return this.path.matcher(path).matches(); } + /** + * Return true if actual value matches expected value using this + * Customization's comparator. Calls to this method should be replaced by + * calls to matches(String prefix, Object actual, Object expected, + * JSONCompareResult result). + * + * @param actual + * JSON value being tested + * @param expected + * expected JSON value + * @return true if actual value matches expected value + */ + @Deprecated public boolean matches(Object actual, Object expected) { return comparator.equal(actual, expected); } + + /** + * Return true if actual value matches expected value using this + * Customization's comparator. The equal method used for comparison depends + * on type of comparator. + * + * @param prefix + * JSON path of the JSON item being tested (only used if + * comparator is a LocationAwareValueMatcher) + * @param actual + * JSON value being tested + * @param expected + * expected JSON value + * @param result + * JSONCompareResult to which match failure may be passed (only + * used if comparator is a LocationAwareValueMatcher) + * @return true if expected and actual equal or any difference has already + * been passed to specified result instance, false otherwise. + * @throws ValueMatcherException + * if expected and actual values not equal and ValueMatcher + * needs to override default comparison failure message that + * would be generated if this method returned false. + */ + public boolean matches(String prefix, Object actual, Object expected, + JSONCompareResult result) throws ValueMatcherException { + if (comparator instanceof LocationAwareValueMatcher) { + return ((LocationAwareValueMatcher)comparator).equal(prefix, actual, expected, result); + } + return comparator.equal(actual, expected); + } } diff --git a/src/main/java/org/skyscreamer/jsonassert/FieldComparisonFailure.java b/src/main/java/org/skyscreamer/jsonassert/FieldComparisonFailure.java index 73d5b4d9..a4746f24 100644 --- a/src/main/java/org/skyscreamer/jsonassert/FieldComparisonFailure.java +++ b/src/main/java/org/skyscreamer/jsonassert/FieldComparisonFailure.java @@ -1,3 +1,17 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; /** diff --git a/src/main/java/org/skyscreamer/jsonassert/JSONAssert.java b/src/main/java/org/skyscreamer/jsonassert/JSONAssert.java index 10ce2c1b..5a2eec18 100644 --- a/src/main/java/org/skyscreamer/jsonassert/JSONAssert.java +++ b/src/main/java/org/skyscreamer/jsonassert/JSONAssert.java @@ -1,7 +1,20 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; import org.skyscreamer.jsonassert.comparator.JSONComparator; @@ -45,28 +58,52 @@ private JSONAssert() {} * @param expectedStr Expected JSON string * @param actual JSONObject to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertEquals(String expectedStr, JSONObject actual, boolean strict) - throws JSONException { + public static void assertEquals(String expectedStr, JSONObject actual, boolean strict) { assertEquals(expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } + + /** + * Asserts that the JSONObject provided matches the expected string. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actual JSONObject to compare + * @param strict Enables strict checking + */ + public static void assertEquals(String message, String expectedStr, JSONObject actual, boolean strict) { + assertEquals(message, expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } /** * Asserts that the JSONObject provided does not match the expected string. If it is it throws an * {@link AssertionError}. * - * @see #assertEquals(String JSONObject, boolean) + * @see #assertEquals(String, JSONObject, boolean) * * @param expectedStr Expected JSON string * @param actual JSONObject to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertNotEquals(String expectedStr, JSONObject actual, boolean strict) - throws JSONException { + public static void assertNotEquals(String expectedStr, JSONObject actual, boolean strict) { assertNotEquals(expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } + + /** + * Asserts that the JSONObject provided does not match the expected string. If it is it throws an + * {@link AssertionError}. + * + * @see #assertEquals(String, JSONObject, boolean) + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actual JSONObject to compare + * @param strict Enables strict checking + */ + public static void assertNotEquals(String message, String expectedStr, JSONObject actual, boolean strict) { + assertNotEquals(message, expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } /** * Asserts that the JSONObject provided matches the expected string. If it isn't it throws an @@ -75,13 +112,25 @@ public static void assertNotEquals(String expectedStr, JSONObject actual, boolea * @param expectedStr Expected JSON string * @param actual JSONObject to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertEquals(String expectedStr, JSONObject actual, JSONCompareMode compareMode) - throws JSONException { + public static void assertEquals(String expectedStr, JSONObject actual, JSONCompareMode compareMode) { + assertEquals("", expectedStr, actual, compareMode); + } + + /** + * Asserts that the JSONObject provided matches the expected string. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actual JSONObject to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertEquals(String message, String expectedStr, JSONObject actual, JSONCompareMode compareMode) + { Object expected = JSONParser.parseJSON(expectedStr); if (expected instanceof JSONObject) { - assertEquals((JSONObject)expected, actual, compareMode); + assertEquals(message, (JSONObject)expected, actual, compareMode); } else { throw new AssertionError("Expecting a JSON array, but passing in a JSON object"); @@ -97,13 +146,27 @@ public static void assertEquals(String expectedStr, JSONObject actual, JSONCompa * @param expectedStr Expected JSON string * @param actual JSONObject to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertNotEquals(String expectedStr, JSONObject actual, JSONCompareMode compareMode) - throws JSONException { + public static void assertNotEquals(String expectedStr, JSONObject actual, JSONCompareMode compareMode) { + assertNotEquals("", expectedStr, actual, compareMode); + } + + /** + * Asserts that the JSONObject provided does not match the expected string. If it is it throws an + * {@link AssertionError}. + * + * @see #assertEquals(String, JSONObject, JSONCompareMode) + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actual JSONObject to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertNotEquals(String message, String expectedStr, JSONObject actual, + JSONCompareMode compareMode) { Object expected = JSONParser.parseJSON(expectedStr); if (expected instanceof JSONObject) { - assertNotEquals((JSONObject) expected, actual, compareMode); + assertNotEquals(message, (JSONObject) expected, actual, compareMode); } else { throw new AssertionError("Expecting a JSON array, but passing in a JSON object"); @@ -117,12 +180,23 @@ public static void assertNotEquals(String expectedStr, JSONObject actual, JSONCo * @param expectedStr Expected JSON string * @param actual JSONArray to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertEquals(String expectedStr, JSONArray actual, boolean strict) - throws JSONException { + public static void assertEquals(String expectedStr, JSONArray actual, boolean strict) { assertEquals(expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } + + /** + * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actual JSONArray to compare + * @param strict Enables strict checking + */ + public static void assertEquals(String message, String expectedStr, JSONArray actual, boolean strict) { + assertEquals(message, expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } /** * Asserts that the JSONArray provided does not match the expected string. If it is it throws an @@ -131,12 +205,23 @@ public static void assertEquals(String expectedStr, JSONArray actual, boolean st * @param expectedStr Expected JSON string * @param actual JSONArray to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertNotEquals(String expectedStr, JSONArray actual, boolean strict) - throws JSONException { + public static void assertNotEquals(String expectedStr, JSONArray actual, boolean strict) { assertNotEquals(expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } + + /** + * Asserts that the JSONArray provided does not match the expected string. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actual JSONArray to compare + * @param strict Enables strict checking + */ + public static void assertNotEquals(String message, String expectedStr, JSONArray actual, boolean strict) { + assertNotEquals(message, expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } /** * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an @@ -145,13 +230,24 @@ public static void assertNotEquals(String expectedStr, JSONArray actual, boolean * @param expectedStr Expected JSON string * @param actual JSONArray to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertEquals(String expectedStr, JSONArray actual, JSONCompareMode compareMode) - throws JSONException { + public static void assertEquals(String expectedStr, JSONArray actual, JSONCompareMode compareMode) { + assertEquals("", expectedStr, actual, compareMode); + } + + /** + * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actual JSONArray to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertEquals(String message, String expectedStr, JSONArray actual, JSONCompareMode compareMode) { Object expected = JSONParser.parseJSON(expectedStr); if (expected instanceof JSONArray) { - assertEquals((JSONArray) expected, actual, compareMode); + assertEquals(message, (JSONArray) expected, actual, compareMode); } else { throw new AssertionError("Expecting a JSON object, but passing in a JSON array"); @@ -165,10 +261,8 @@ public static void assertEquals(String expectedStr, JSONArray actual, JSONCompar * @param expectedStr Expected JSON string * @param actual JSONArray to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertNotEquals(String expectedStr, JSONArray actual, JSONCompareMode compareMode) - throws JSONException { + public static void assertNotEquals(String expectedStr, JSONArray actual, JSONCompareMode compareMode) { Object expected = JSONParser.parseJSON(expectedStr); if (expected instanceof JSONArray) { assertNotEquals((JSONArray) expected, actual, compareMode); @@ -177,6 +271,26 @@ public static void assertNotEquals(String expectedStr, JSONArray actual, JSONCom throw new AssertionError("Expecting a JSON object, but passing in a JSON array"); } } + + /** + * Asserts that the JSONArray provided does not match the expected string. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actual JSONArray to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertNotEquals(String message, String expectedStr, JSONArray actual, + JSONCompareMode compareMode) { + Object expected = JSONParser.parseJSON(expectedStr); + if (expected instanceof JSONArray) { + assertNotEquals(message, (JSONArray) expected, actual, compareMode); + } + else { + throw new AssertionError("Expecting a JSON object, but passing in a JSON array"); + } + } /** * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an @@ -185,12 +299,23 @@ public static void assertNotEquals(String expectedStr, JSONArray actual, JSONCom * @param expectedStr Expected JSON string * @param actualStr String to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertEquals(String expectedStr, String actualStr, boolean strict) - throws JSONException { + public static void assertEquals(String expectedStr, String actualStr, boolean strict) { assertEquals(expectedStr, actualStr, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } + + /** + * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actualStr String to compare + * @param strict Enables strict checking + */ + public static void assertEquals(String message, String expectedStr, String actualStr, boolean strict) { + assertEquals(message, expectedStr, actualStr, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } /** * Asserts that the JSONArray provided does not match the expected string. If it is it throws an @@ -199,12 +324,23 @@ public static void assertEquals(String expectedStr, String actualStr, boolean st * @param expectedStr Expected JSON string * @param actualStr String to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertNotEquals(String expectedStr, String actualStr, boolean strict) - throws JSONException { + public static void assertNotEquals(String expectedStr, String actualStr, boolean strict) { assertNotEquals(expectedStr, actualStr, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } + + /** + * Asserts that the JSONArray provided does not match the expected string. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actualStr String to compare + * @param strict Enables strict checking + */ + public static void assertNotEquals(String message, String expectedStr, String actualStr, boolean strict) { + assertNotEquals(message, expectedStr, actualStr, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } /** * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an @@ -213,13 +349,30 @@ public static void assertNotEquals(String expectedStr, String actualStr, boolean * @param expectedStr Expected JSON string * @param actualStr String to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertEquals(String expectedStr, String actualStr, JSONCompareMode compareMode) - throws JSONException { + public static void assertEquals(String expectedStr, String actualStr, JSONCompareMode compareMode) { + assertEquals("", expectedStr, actualStr, compareMode); + } + + /** + * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actualStr String to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertEquals(String message, String expectedStr, String actualStr, JSONCompareMode compareMode) { + if (expectedStr==actualStr) return; + if (expectedStr==null){ + throw new AssertionError("Expected string is null."); + }else if (actualStr==null){ + throw new AssertionError("Actual string is null."); + } JSONCompareResult result = JSONCompare.compareJSON(expectedStr, actualStr, compareMode); if (result.failed()) { - throw new AssertionError(result.getMessage()); + throw new AssertionError(getCombinedMessage(message, result.getMessage())); } } @@ -230,13 +383,25 @@ public static void assertEquals(String expectedStr, String actualStr, JSONCompar * @param expectedStr Expected JSON string * @param actualStr String to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertNotEquals(String expectedStr, String actualStr, JSONCompareMode compareMode) - throws JSONException { + public static void assertNotEquals(String expectedStr, String actualStr, JSONCompareMode compareMode) { + assertNotEquals("", expectedStr, actualStr, compareMode); + } + + /** + * Asserts that the JSONArray provided does not match the expected string. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actualStr String to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertNotEquals(String message, String expectedStr, String actualStr, + JSONCompareMode compareMode) { JSONCompareResult result = JSONCompare.compareJSON(expectedStr, actualStr, compareMode); if (result.passed()) { - throw new AssertionError(result.getMessage()); + throw new AssertionError(getCombinedMessage(message, result.getMessage())); } } @@ -247,13 +412,25 @@ public static void assertNotEquals(String expectedStr, String actualStr, JSONCom * @param expectedStr Expected JSON string * @param actualStr String to compare * @param comparator Comparator - * @throws JSONException */ - public static void assertEquals(String expectedStr, String actualStr, JSONComparator comparator) - throws JSONException { + public static void assertEquals(String expectedStr, String actualStr, JSONComparator comparator) { + assertEquals("", expectedStr, actualStr, comparator); + + } + + /** + * Asserts that the json string provided matches the expected string. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actualStr String to compare + * @param comparator Comparator + */ + public static void assertEquals(String message, String expectedStr, String actualStr, JSONComparator comparator) { JSONCompareResult result = JSONCompare.compareJSON(expectedStr, actualStr, comparator); if (result.failed()) { - throw new AssertionError(result.getMessage()); + throw new AssertionError(getCombinedMessage(message, result.getMessage())); } } @@ -264,16 +441,85 @@ public static void assertEquals(String expectedStr, String actualStr, JSONCompar * @param expectedStr Expected JSON string * @param actualStr String to compare * @param comparator Comparator - * @throws JSONException */ - public static void assertNotEquals(String expectedStr, String actualStr, JSONComparator comparator) - throws JSONException { + public static void assertNotEquals(String expectedStr, String actualStr, JSONComparator comparator) { + assertNotEquals("", expectedStr, actualStr, comparator); + } + + /** + * Asserts that the json string provided does not match the expected string. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expectedStr Expected JSON string + * @param actualStr String to compare + * @param comparator Comparator + */ + public static void assertNotEquals(String message, String expectedStr, String actualStr, + JSONComparator comparator) { JSONCompareResult result = JSONCompare.compareJSON(expectedStr, actualStr, comparator); if (result.passed()) { - throw new AssertionError(result.getMessage()); + throw new AssertionError(getCombinedMessage(message, result.getMessage())); } } + /** + * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an + * {@link AssertionError}. + * + * @param expected Expected JSONObject + * @param actual JSONObject to compare + * @param comparator Comparator + */ + public static void assertEquals(JSONObject expected, JSONObject actual, JSONComparator comparator) { + assertEquals("", expected, actual, comparator); + } + + /** + * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONObject + * @param actual JSONObject to compare + * @param comparator Comparator + */ + public static void assertEquals(String message, JSONObject expected, JSONObject actual, JSONComparator comparator) { + JSONCompareResult result = JSONCompare.compareJSON(expected, actual, comparator); + if (result.failed()) { + throw new AssertionError(getCombinedMessage(message, result.getMessage())); + } + } + + /** + * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an + * {@link AssertionError}. + * + * @param expected Expected JSONObject + * @param actual JSONObject to compare + * @param comparator Comparator + */ + public static void assertNotEquals(JSONObject expected, JSONObject actual, JSONComparator comparator) { + assertNotEquals("", expected, actual, comparator); + } + + /** + * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONObject + * @param actual JSONObject to compare + * @param comparator Comparator + */ + public static void assertNotEquals(String message, JSONObject expected, JSONObject actual, + JSONComparator comparator) { + JSONCompareResult result = JSONCompare.compareJSON(expected, actual, comparator); + if (result.passed()) { + throw new AssertionError(getCombinedMessage(message, result.getMessage())); + } + } + /** * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an * {@link AssertionError}. @@ -281,12 +527,23 @@ public static void assertNotEquals(String expectedStr, String actualStr, JSONCom * @param expected Expected JSONObject * @param actual JSONObject to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertEquals(JSONObject expected, JSONObject actual, boolean strict) - throws JSONException { + public static void assertEquals(JSONObject expected, JSONObject actual, boolean strict) { assertEquals(expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } + + /** + * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONObject + * @param actual JSONObject to compare + * @param strict Enables strict checking + */ + public static void assertEquals(String message, JSONObject expected, JSONObject actual, boolean strict) { + assertEquals(message, expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } /** * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an @@ -295,12 +552,23 @@ public static void assertEquals(JSONObject expected, JSONObject actual, boolean * @param expected Expected JSONObject * @param actual JSONObject to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertNotEquals(JSONObject expected, JSONObject actual, boolean strict) - throws JSONException { + public static void assertNotEquals(JSONObject expected, JSONObject actual, boolean strict) { assertNotEquals(expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } + + /** + * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONObject + * @param actual JSONObject to compare + * @param strict Enables strict checking + */ + public static void assertNotEquals(String message, JSONObject expected, JSONObject actual, boolean strict) { + assertNotEquals(message, expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } /** * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an @@ -309,14 +577,25 @@ public static void assertNotEquals(JSONObject expected, JSONObject actual, boole * @param expected Expected JSONObject * @param actual JSONObject to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertEquals(JSONObject expected, JSONObject actual, JSONCompareMode compareMode) - throws JSONException - { + public static void assertEquals(JSONObject expected, JSONObject actual, JSONCompareMode compareMode) { + assertEquals("", expected, actual, compareMode); + } + + /** + * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONObject + * @param actual JSONObject to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertEquals(String message, JSONObject expected, JSONObject actual, + JSONCompareMode compareMode) { JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); if (result.failed()) { - throw new AssertionError(result.getMessage()); + throw new AssertionError(getCombinedMessage(message, result.getMessage())); } } @@ -327,14 +606,25 @@ public static void assertEquals(JSONObject expected, JSONObject actual, JSONComp * @param expected Expected JSONObject * @param actual JSONObject to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertNotEquals(JSONObject expected, JSONObject actual, JSONCompareMode compareMode) - throws JSONException - { + public static void assertNotEquals(JSONObject expected, JSONObject actual, JSONCompareMode compareMode) { + assertNotEquals("", expected, actual, compareMode); + } + + /** + * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONObject + * @param actual JSONObject to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertNotEquals(String message, JSONObject expected, JSONObject actual, + JSONCompareMode compareMode) { JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); if (result.passed()) { - throw new AssertionError(result.getMessage()); + throw new AssertionError(getCombinedMessage(message, result.getMessage())); } } @@ -345,11 +635,22 @@ public static void assertNotEquals(JSONObject expected, JSONObject actual, JSONC * @param expected Expected JSONArray * @param actual JSONArray to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertEquals(JSONArray expected, JSONArray actual, boolean strict) - throws JSONException { - assertEquals(expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + public static void assertEquals(JSONArray expected, JSONArray actual, boolean strict) { + assertEquals("", expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } + + /** + * Asserts that the JSONArray provided matches the expected JSONArray. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONArray + * @param actual JSONArray to compare + * @param strict Enables strict checking + */ + public static void assertEquals(String message, JSONArray expected, JSONArray actual, boolean strict) { + assertEquals(message, expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } /** @@ -359,12 +660,23 @@ public static void assertEquals(JSONArray expected, JSONArray actual, boolean st * @param expected Expected JSONArray * @param actual JSONArray to compare * @param strict Enables strict checking - * @throws JSONException */ - public static void assertNotEquals(JSONArray expected, JSONArray actual, boolean strict) - throws JSONException { + public static void assertNotEquals(JSONArray expected, JSONArray actual, boolean strict) { assertNotEquals(expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); } + + /** + * Asserts that the JSONArray provided does not match the expected JSONArray. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONArray + * @param actual JSONArray to compare + * @param strict Enables strict checking + */ + public static void assertNotEquals(String message, JSONArray expected, JSONArray actual, boolean strict) { + assertNotEquals(message, expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); + } /** * Asserts that the JSONArray provided matches the expected JSONArray. If it isn't it throws an @@ -373,13 +685,24 @@ public static void assertNotEquals(JSONArray expected, JSONArray actual, boolean * @param expected Expected JSONArray * @param actual JSONArray to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertEquals(JSONArray expected, JSONArray actual, JSONCompareMode compareMode) - throws JSONException { + public static void assertEquals(JSONArray expected, JSONArray actual, JSONCompareMode compareMode) { + assertEquals("", expected, actual, compareMode); + } + + /** + * Asserts that the JSONArray provided matches the expected JSONArray. If it isn't it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONArray + * @param actual JSONArray to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertEquals(String message, JSONArray expected, JSONArray actual, JSONCompareMode compareMode) { JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); if (result.failed()) { - throw new AssertionError(result.getMessage()); + throw new AssertionError(getCombinedMessage(message, result.getMessage())); } } @@ -390,13 +713,36 @@ public static void assertEquals(JSONArray expected, JSONArray actual, JSONCompar * @param expected Expected JSONArray * @param actual JSONArray to compare * @param compareMode Specifies which comparison mode to use - * @throws JSONException */ - public static void assertNotEquals(JSONArray expected, JSONArray actual, JSONCompareMode compareMode) - throws JSONException { + public static void assertNotEquals(JSONArray expected, JSONArray actual, JSONCompareMode compareMode) { + assertNotEquals("", expected, actual, compareMode); + } + + /** + * Asserts that the JSONArray provided does not match the expected JSONArray. If it is it throws an + * {@link AssertionError}. + * + * @param message Error message to be displayed in case of assertion failure + * @param expected Expected JSONArray + * @param actual JSONArray to compare + * @param compareMode Specifies which comparison mode to use + */ + public static void assertNotEquals(String message, JSONArray expected, JSONArray actual, + JSONCompareMode compareMode) { JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); if (result.passed()) { - throw new AssertionError(result.getMessage()); + throw new AssertionError(getCombinedMessage(message, result.getMessage())); + } + } + + private static String getCombinedMessage(String message1, String message2) { + String combinedMessage = ""; + + if(message1 == null || "".equals(message1)) { + combinedMessage = message2; + } else { + combinedMessage = message1 + " " + message2; } + return combinedMessage; } } diff --git a/src/main/java/org/skyscreamer/jsonassert/JSONCompare.java b/src/main/java/org/skyscreamer/jsonassert/JSONCompare.java index fc41bf4f..c17115df 100644 --- a/src/main/java/org/skyscreamer/jsonassert/JSONCompare.java +++ b/src/main/java/org/skyscreamer/jsonassert/JSONCompare.java @@ -1,7 +1,20 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; import org.json.JSONString; import org.skyscreamer.jsonassert.comparator.DefaultComparator; @@ -27,11 +40,9 @@ private static JSONComparator getComparatorForMode(JSONCompareMode mode) { * @param actualStr JSON string to compare * @param comparator Comparator to use * @return result of the comparison - * @throws JSONException * @throws IllegalArgumentException when type of expectedStr doesn't match the type of actualStr */ - public static JSONCompareResult compareJSON(String expectedStr, String actualStr, JSONComparator comparator) - throws JSONException { + public static JSONCompareResult compareJSON(String expectedStr, String actualStr, JSONComparator comparator) { Object expected = JSONParser.parseJSON(expectedStr); Object actual = JSONParser.parseJSON(actualStr); if ((expected instanceof JSONObject) && (actual instanceof JSONObject)) { @@ -58,10 +69,8 @@ else if (expected instanceof JSONObject) { * @param actual actual json object * @param comparator comparator to use * @return result of the comparison - * @throws JSONException */ - public static JSONCompareResult compareJSON(JSONObject expected, JSONObject actual, JSONComparator comparator) - throws JSONException { + public static JSONCompareResult compareJSON(JSONObject expected, JSONObject actual, JSONComparator comparator) { return comparator.compareJSON(expected, actual); } @@ -72,10 +81,8 @@ public static JSONCompareResult compareJSON(JSONObject expected, JSONObject actu * @param actual actual json array * @param comparator comparator to use * @return result of the comparison - * @throws JSONException */ - public static JSONCompareResult compareJSON(JSONArray expected, JSONArray actual, JSONComparator comparator) - throws JSONException { + public static JSONCompareResult compareJSON(JSONArray expected, JSONArray actual, JSONComparator comparator) { return comparator.compareJSON(expected, actual); } @@ -85,6 +92,7 @@ public static JSONCompareResult compareJSON(JSONArray expected, JSONArray actual * * @param expected Expected {@code JSONstring} * @param actual {@code JSONstring} to compare + * @return result of the comparison */ public static JSONCompareResult compareJson(final JSONString expected, final JSONString actual) { final JSONCompareResult result = new JSONCompareResult(); @@ -102,10 +110,9 @@ public static JSONCompareResult compareJson(final JSONString expected, final JSO * @param expectedStr Expected JSON string * @param actualStr JSON string to compare * @param mode Defines comparison behavior - * @throws JSONException + * @return result of the comparison */ - public static JSONCompareResult compareJSON(String expectedStr, String actualStr, JSONCompareMode mode) - throws JSONException { + public static JSONCompareResult compareJSON(String expectedStr, String actualStr, JSONCompareMode mode) { return compareJSON(expectedStr, actualStr, getComparatorForMode(mode)); } @@ -115,10 +122,9 @@ public static JSONCompareResult compareJSON(String expectedStr, String actualStr * @param expected Expected JSONObject * @param actual JSONObject to compare * @param mode Defines comparison behavior - * @throws JSONException + * @return result of the comparison */ - public static JSONCompareResult compareJSON(JSONObject expected, JSONObject actual, JSONCompareMode mode) - throws JSONException { + public static JSONCompareResult compareJSON(JSONObject expected, JSONObject actual, JSONCompareMode mode) { return compareJSON(expected, actual, getComparatorForMode(mode)); } @@ -129,10 +135,9 @@ public static JSONCompareResult compareJSON(JSONObject expected, JSONObject actu * @param expected Expected JSONArray * @param actual JSONArray to compare * @param mode Defines comparison behavior - * @throws JSONException + * @return result of the comparison */ - public static JSONCompareResult compareJSON(JSONArray expected, JSONArray actual, JSONCompareMode mode) - throws JSONException { + public static JSONCompareResult compareJSON(JSONArray expected, JSONArray actual, JSONCompareMode mode) { return compareJSON(expected, actual, getComparatorForMode(mode)); } diff --git a/src/main/java/org/skyscreamer/jsonassert/JSONCompareMode.java b/src/main/java/org/skyscreamer/jsonassert/JSONCompareMode.java index a4df7e07..8185b065 100644 --- a/src/main/java/org/skyscreamer/jsonassert/JSONCompareMode.java +++ b/src/main/java/org/skyscreamer/jsonassert/JSONCompareMode.java @@ -1,3 +1,17 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; /** @@ -5,6 +19,9 @@ * Each mode encapsulates two underlying behaviors: extensibility and strict ordering.

* * + * * * * @@ -52,7 +69,7 @@ public enum JSONCompareMode { private final boolean _extensible; private final boolean _strictOrder; - private JSONCompareMode(boolean extensible, boolean strictOrder) { + JSONCompareMode(boolean extensible, boolean strictOrder) { _extensible = extensible; _strictOrder = strictOrder; } @@ -76,6 +93,7 @@ public boolean hasStrictOrder() { /** * Get the equivalent {@code JSONCompareMode} with or without strict ordering. * + * @param strictOrdering if true, requires strict ordering of array elements * @return the equivalent {@code JSONCompareMode} */ public JSONCompareMode withStrictOrdering(boolean strictOrdering) { @@ -89,6 +107,7 @@ public JSONCompareMode withStrictOrdering(boolean strictOrdering) { /** * Get the equivalent {@code JSONCompareMode} with or without extensibility. * + * @param extensible if true, allows keys in actual that don't appear in expected * @return the equivalent {@code JSONCompareMode} */ public JSONCompareMode withExtensible(boolean extensible) { diff --git a/src/main/java/org/skyscreamer/jsonassert/JSONCompareResult.java b/src/main/java/org/skyscreamer/jsonassert/JSONCompareResult.java index 3b821b46..55672099 100644 --- a/src/main/java/org/skyscreamer/jsonassert/JSONCompareResult.java +++ b/src/main/java/org/skyscreamer/jsonassert/JSONCompareResult.java @@ -1,3 +1,17 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; import java.util.ArrayList; @@ -17,6 +31,8 @@ public class JSONCompareResult { private Object _expected; private Object _actual; private final List _fieldFailures = new ArrayList(); + private final List _fieldMissing = new ArrayList(); + private final List _fieldUnexpected = new ArrayList(); /** * Default constructor. @@ -56,10 +72,27 @@ public String getMessage() { /** * Get the list of failures on field comparisons + * @return list of comparsion failures */ public List getFieldFailures() { return Collections.unmodifiableList(_fieldFailures); } + + /** + * Get the list of missed on field comparisons + * @return list of comparsion failures + */ + public List getFieldMissing() { + return Collections.unmodifiableList(_fieldMissing); + } + + /** + * Get the list of failures on field comparisons + * @return list of comparsion failures + */ + public List getFieldUnexpected() { + return Collections.unmodifiableList(_fieldUnexpected); + } /** * Actual field value @@ -69,6 +102,7 @@ public List getFieldFailures() { * particular field * @deprecated Superseded by {@link #getFieldFailures()} */ + @Deprecated public Object getActual() { return _actual; } @@ -81,16 +115,34 @@ public Object getActual() { * particular field * @deprecated Superseded by {@link #getFieldFailures()} */ + @Deprecated public Object getExpected() { return _expected; } /** * Check if comparison failed on any particular fields + * @return true if there are field failures */ public boolean isFailureOnField() { return !_fieldFailures.isEmpty(); } + + /** + * Check if comparison failed with missing on any particular fields + * @return true if an expected field is missing + */ + public boolean isMissingOnField() { + return !_fieldMissing.isEmpty(); + } + + /** + * Check if comparison failed with unexpected on any particular fields + * @return true if an unexpected field is in the result + */ + public boolean isUnexpectedOnField() { + return !_fieldUnexpected.isEmpty(); + } /** * Dot-separated path the the field that failed comparison @@ -99,6 +151,7 @@ public boolean isFailureOnField() { * not fail on a particular field * @deprecated Superseded by {@link #getFieldFailures()} */ + @Deprecated public String getField() { return _field; } @@ -117,6 +170,7 @@ public void fail(String message) { * @param field Which field failed * @param expected Expected result * @param actual Actual result + * @return result of comparision */ public JSONCompareResult fail(String field, Object expected, Object actual) { _fieldFailures.add(new FieldComparisonFailure(field, expected, actual)); @@ -127,6 +181,17 @@ public JSONCompareResult fail(String field, Object expected, Object actual) { return this; } + /** + * Identify that the comparison failed + * @param field Which field failed + * @param exception exception containing details of match failure + * @return result of comparision + */ + public JSONCompareResult fail(String field, ValueMatcherException exception) { + fail(field + ": " + exception.getMessage(), exception.getExpected(), exception.getActual()); + return this; + } + private String formatFailureMessage(String field, Object expected, Object actual) { return field + "\nExpected: " @@ -136,7 +201,14 @@ private String formatFailureMessage(String field, Object expected, Object actual + "\n"; } + /** + * Identify the missing field + * @param field missing field + * @param expected expected result + * @return result of comparison + */ public JSONCompareResult missing(String field, Object expected) { + _fieldMissing.add(new FieldComparisonFailure(field, expected, null)); fail(formatMissing(field, expected)); return this; } @@ -148,15 +220,22 @@ private String formatMissing(String field, Object expected) { + "\n but none found\n"; } - public JSONCompareResult unexpected(String field, Object value) { - fail(formatUnexpected(field, value)); + /** + * Identify unexpected field + * @param field unexpected field + * @param actual actual result + * @return result of comparison + */ + public JSONCompareResult unexpected(String field, Object actual) { + _fieldUnexpected.add(new FieldComparisonFailure(field, null, actual)); + fail(formatUnexpected(field, actual)); return this; } - private String formatUnexpected(String field, Object value) { + private String formatUnexpected(String field, Object actual) { return field + "\nUnexpected: " - + describe(value) + + describe(actual) + "\n"; } @@ -166,7 +245,7 @@ private static String describe(Object value) { } else if (value instanceof JSONObject) { return "a JSON object"; } else { - return value.toString(); + return String.valueOf(value); } } diff --git a/src/main/java/org/skyscreamer/jsonassert/JSONParser.java b/src/main/java/org/skyscreamer/jsonassert/JSONParser.java index 45df77c1..a70af96c 100644 --- a/src/main/java/org/skyscreamer/jsonassert/JSONParser.java +++ b/src/main/java/org/skyscreamer/jsonassert/JSONParser.java @@ -1,3 +1,17 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; import org.json.JSONArray; @@ -22,9 +36,8 @@ private JSONParser() {} * * @param s Raw JSON string to be parsed * @return JSONObject or JSONArray - * @throws JSONException */ - public static Object parseJSON(final String s) throws JSONException { + public static Object parseJSON(final String s) { if (s.trim().startsWith("{")) { return new JSONObject(s); } diff --git a/src/main/java/org/skyscreamer/jsonassert/LocationAwareValueMatcher.java b/src/main/java/org/skyscreamer/jsonassert/LocationAwareValueMatcher.java new file mode 100644 index 00000000..cc2b7449 --- /dev/null +++ b/src/main/java/org/skyscreamer/jsonassert/LocationAwareValueMatcher.java @@ -0,0 +1,54 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +/** + * + */ +package org.skyscreamer.jsonassert; + +/** + * A ValueMatcher extension that provides location in form of prefix to the equals method. + * + * @author Duncan Mackinder + * @param Generic Type + */ +public interface LocationAwareValueMatcher extends ValueMatcher { + + /** + * Match actual value with expected value. If match fails any of the + * following may occur, return false, pass failure details to specified + * JSONCompareResult and return true, or throw ValueMatcherException + * containing failure details. Passing failure details to JSONCompareResult + * or returning via ValueMatcherException enables more useful failure + * description for cases where expected value depends entirely or in part on + * configuration of the ValueMatcher and therefore expected value passed to + * this method will not give a useful indication of expected value. + * + * @param prefix + * JSON path of the JSON item being tested + * @param actual + * JSON value being tested + * @param expected + * expected JSON value + * @param result + * JSONCompareResult to which match failure may be passed + * @return true if expected and actual equal or any difference has already + * been passed to specified result instance, false otherwise. + * @throws ValueMatcherException + * if expected and actual values not equal and ValueMatcher + * needs to override default comparison failure message that + * would be generated if this method returned false. + */ + boolean equal(String prefix, T actual, T expected, JSONCompareResult result) throws ValueMatcherException; +} diff --git a/src/main/java/org/skyscreamer/jsonassert/RegularExpressionValueMatcher.java b/src/main/java/org/skyscreamer/jsonassert/RegularExpressionValueMatcher.java new file mode 100644 index 00000000..19a4ef1d --- /dev/null +++ b/src/main/java/org/skyscreamer/jsonassert/RegularExpressionValueMatcher.java @@ -0,0 +1,96 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.skyscreamer.jsonassert; + +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.skyscreamer.jsonassert.ValueMatcher; + +/** + * A JSONassert value matcher that matches actual value to regular expression. + * If non-null regular expression passed to constructor, then all actual values + * will be compared against this constant pattern, ignoring any expected value + * passed to equal method. If null regular expression passed to constructor, + * then expected value passed to equals method will be used to dynamically + * specify regular expression pattern that actual value must match. + * + * @author Duncan Mackinder + * @param Generic Type + */ +public class RegularExpressionValueMatcher implements ValueMatcher { + + private final Pattern expectedPattern; + + /** + * Create RegularExpressionValueMatcher in which the pattern the actual + * value must match with be specified dynamically from the expected string + * passed to this matcher in the equals method. + */ + public RegularExpressionValueMatcher() { + this(null); + } + + /** + * Create RegularExpressionValueMatcher with specified pattern. If pattern + * is not null, it must be a valid regular expression that defines a + * constant expected pattern that every actual value must match (in this + * case the expected value passed to equal method will be ignored). If + * pattern is null, the pattern the actual value must match with be + * specified dynamically from the expected string passed to this matcher in + * the equals method. + * + * @param pattern + * if non null, regular expression pattern which all actual + * values this matcher is applied to must match. If null, this + * matcher will apply pattern specified dynamically via the + * expected parameter to the equal method. + * @throws IllegalArgumentException + * if pattern is non-null and not a valid regular expression. + */ + public RegularExpressionValueMatcher(String pattern) throws IllegalArgumentException { + try { + expectedPattern = pattern == null ? null : Pattern.compile(pattern); + } + catch (PatternSyntaxException e) { + throw new IllegalArgumentException("Constant expected pattern invalid: " + e.getMessage(), e); + } + } + + @Override + public boolean equal(T actual, T expected) { + String actualString = actual.toString(); + String expectedString = expected.toString(); + try { + Pattern pattern = isStaticPattern() ? expectedPattern : Pattern + .compile(expectedString); + if (!pattern.matcher(actualString).matches()) { + throw new ValueMatcherException(getPatternType() + " expected pattern did not match value", pattern.toString(), actualString); + } + } + catch (PatternSyntaxException e) { + throw new ValueMatcherException(getPatternType() + " expected pattern invalid: " + e.getMessage(), e, expectedString, actualString); + } + return true; + } + + private boolean isStaticPattern() { + return expectedPattern != null; + } + + private String getPatternType() { + return isStaticPattern()? "Constant": "Dynamic"; + } +} diff --git a/src/main/java/org/skyscreamer/jsonassert/ValueMatcher.java b/src/main/java/org/skyscreamer/jsonassert/ValueMatcher.java index 58a86cf9..a05691a1 100644 --- a/src/main/java/org/skyscreamer/jsonassert/ValueMatcher.java +++ b/src/main/java/org/skyscreamer/jsonassert/ValueMatcher.java @@ -1,7 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; +/** + * Represents a value matcher that can compare two objects for equality. + * + * @param the object type to compare + */ public interface ValueMatcher { + /** + * Compares the two provided objects whether they are equal. + * + * @param o1 the first object to check + * @param o2 the object to check the first against + * @return true if the objects are equal, false otherwise + */ boolean equal(T o1, T o2); } diff --git a/src/main/java/org/skyscreamer/jsonassert/ValueMatcherException.java b/src/main/java/org/skyscreamer/jsonassert/ValueMatcherException.java new file mode 100644 index 00000000..19807722 --- /dev/null +++ b/src/main/java/org/skyscreamer/jsonassert/ValueMatcherException.java @@ -0,0 +1,78 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.skyscreamer.jsonassert; + +/** + * Exception that may be thrown by ValueMatcher subclasses to provide more detail on why matches method failed. + * + * @author Duncan Mackinder + * + */ +public class ValueMatcherException extends RuntimeException { + private static final long serialVersionUID = 1L; + + private final String expected; + + private final String actual; + + /** + * Create new ValueMatcherException + * + * @param message + * description of exception + * @param expected + * value expected by ValueMatcher + * @param actual + * value being tested by ValueMatcher + */ + public ValueMatcherException(String message, String expected, String actual) { + super(message); + this.expected = expected; + this.actual = actual; + } + + /** + * Create new ValueMatcherException + * + * @param message + * description of exception + * @param cause + * cause of ValueMatcherException + * @param expected + * value expected by ValueMatcher + * @param actual + * value being tested by ValueMatcher + */ + public ValueMatcherException(String message, Throwable cause, String expected, String actual) { + super(message, cause); + this.expected = expected; + this.actual = actual; + } + + /** + * @return the expected value + */ + public String getExpected() { + return expected; + } + + /** + * @return the actual value + */ + public String getActual() { + return actual; + } + +} diff --git a/src/main/java/org/skyscreamer/jsonassert/comparator/AbstractComparator.java b/src/main/java/org/skyscreamer/jsonassert/comparator/AbstractComparator.java index 037bb6af..190e47ea 100644 --- a/src/main/java/org/skyscreamer/jsonassert/comparator/AbstractComparator.java +++ b/src/main/java/org/skyscreamer/jsonassert/comparator/AbstractComparator.java @@ -1,7 +1,20 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert.comparator; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; import org.skyscreamer.jsonassert.JSONCompareResult; @@ -11,19 +24,26 @@ /** * This class provides a skeletal implementation of the {@link JSONComparator} - * interface, to minimize the effort required to implement this interface.

+ * interface, to minimize the effort required to implement this interface. + * + * */ public abstract class AbstractComparator implements JSONComparator { + /** + * Default constructor + */ + public AbstractComparator() { + } + /** * Compares JSONObject provided to the expected JSONObject, and returns the results of the comparison. * * @param expected Expected JSONObject * @param actual JSONObject to compare - * @throws JSONException */ @Override - public final JSONCompareResult compareJSON(JSONObject expected, JSONObject actual) throws JSONException { + public final JSONCompareResult compareJSON(JSONObject expected, JSONObject actual) { JSONCompareResult result = new JSONCompareResult(); compareJSON("", expected, actual, result); return result; @@ -34,15 +54,20 @@ public final JSONCompareResult compareJSON(JSONObject expected, JSONObject actua * * @param expected Expected JSONArray * @param actual JSONArray to compare - * @throws JSONException */ @Override - public final JSONCompareResult compareJSON(JSONArray expected, JSONArray actual) throws JSONException { + public final JSONCompareResult compareJSON(JSONArray expected, JSONArray actual) { JSONCompareResult result = new JSONCompareResult(); compareJSONArray("", expected, actual, result); return result; } + /** + * @param prefix + * @param expected + * @param actual + * @param result + */ protected void checkJsonObjectKeysActualInExpected(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) { Set actualKeys = getKeys(actual); for (String key : actualKeys) { @@ -52,7 +77,14 @@ protected void checkJsonObjectKeysActualInExpected(String prefix, JSONObject exp } } - protected void checkJsonObjectKeysExpectedInActual(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) throws JSONException { + /** + * + * @param prefix + * @param expected + * @param actual + * @param result + */ + protected void checkJsonObjectKeysExpectedInActual(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) { Set expectedKeys = getKeys(expected); for (String key : expectedKeys) { Object expectedValue = expected.get(key); @@ -65,7 +97,7 @@ protected void checkJsonObjectKeysExpectedInActual(String prefix, JSONObject exp } } - protected void compareJSONArrayOfJsonObjects(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException { + protected void compareJSONArrayOfJsonObjects(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) { String uniqueKey = findUniqueKey(expected); if (uniqueKey == null || !isUsableAsUniqueKey(uniqueKey, actual)) { // An expensive last resort @@ -90,7 +122,7 @@ protected void compareJSONArrayOfJsonObjects(String key, JSONArray expected, JSO } } - protected void compareJSONArrayOfSimpleValues(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException { + protected void compareJSONArrayOfSimpleValues(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) { Map expectedCount = JSONCompareUtil.getCardinalityMap(jsonArrayToList(expected)); Map actualCount = JSONCompareUtil.getCardinalityMap(jsonArrayToList(actual)); for (Object o : expectedCount.keySet()) { @@ -108,10 +140,10 @@ protected void compareJSONArrayOfSimpleValues(String key, JSONArray expected, JS } } - protected void compareJSONArrayWithStrictOrder(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException { + protected void compareJSONArrayWithStrictOrder(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) { for (int i = 0; i < expected.length(); ++i) { - Object expectedValue = expected.get(i); - Object actualValue = actual.get(i); + Object expectedValue = JSONCompareUtil.getObjectOrNull(expected, i); + Object actualValue = JSONCompareUtil.getObjectOrNull(actual, i); compareValues(key + "[" + i + "]", expectedValue, actualValue, result); } } @@ -121,13 +153,20 @@ protected void compareJSONArrayWithStrictOrder(String key, JSONArray expected, J // This is expensive (O(n^2) -- yuck), but may be the only resort for some cases with loose array ordering, and no // easy way to uniquely identify each element. protected void recursivelyCompareJSONArray(String key, JSONArray expected, JSONArray actual, - JSONCompareResult result) throws JSONException { + JSONCompareResult result) { Set matched = new HashSet(); for (int i = 0; i < expected.length(); ++i) { - Object expectedElement = expected.get(i); + Object expectedElement = JSONCompareUtil.getObjectOrNull(expected, i); boolean matchFound = false; for (int j = 0; j < actual.length(); ++j) { - Object actualElement = actual.get(j); + Object actualElement = JSONCompareUtil.getObjectOrNull(actual, j); + if (expectedElement == actualElement) { + matchFound = true; + break; + } + if ((expectedElement == null && actualElement != null) || (expectedElement != null && actualElement == null)) { + continue; + } if (matched.contains(j) || !actualElement.getClass().equals(expectedElement.getClass())) { continue; } diff --git a/src/main/java/org/skyscreamer/jsonassert/comparator/ArraySizeComparator.java b/src/main/java/org/skyscreamer/jsonassert/comparator/ArraySizeComparator.java new file mode 100644 index 00000000..7e348b82 --- /dev/null +++ b/src/main/java/org/skyscreamer/jsonassert/comparator/ArraySizeComparator.java @@ -0,0 +1,120 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.skyscreamer.jsonassert.comparator; + +import java.text.MessageFormat; + +import org.json.JSONArray; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.skyscreamer.jsonassert.JSONCompareResult; + +/** + * A JSONAssert array size comparator. + * + *

Some typical usage idioms are listed below.

+ * + *

Assuming JSON to be verified is held in String variable ARRAY_OF_JSONOBJECTS and contains:

+ * + * {a:[7, 8, 9]} + * + *

then:

+ * + *

To verify that array 'a' contains 3 elements:

+ * + * + * JSONAssert.assertEquals("{a:[3]}", ARRAY_OF_JSONOBJECTS, new ArraySizeComparator(JSONCompareMode.LENIENT)); + * + * + *

To verify that array 'a' contains between 2 and 6 elements:

+ * + * + * JSONAssert.assertEquals("{a:[2,6]}", ARRAY_OF_JSONOBJECTS, new ArraySizeComparator(JSONCompareMode.LENIENT)); + * + * + * @author Duncan Mackinder + * + */ +public class ArraySizeComparator extends DefaultComparator { + + /** + * Create new ArraySizeComparator. + * + * @param mode + * comparison mode, has no impact on ArraySizeComparator but is + * used by instance of superclass DefaultComparator to control + * comparison of JSON items other than arrays. + */ + public ArraySizeComparator(JSONCompareMode mode) { + super(mode); + } + + /** + * Expected array should consist of either 1 or 2 integer values that define + * maximum and minimum valid lengths of the actual array. If expected array + * contains a single integer value, then the actual array must contain + * exactly that number of elements. + */ + @Override + public void compareJSONArray(String prefix, JSONArray expected, + JSONArray actual, JSONCompareResult result) { + String arrayPrefix = prefix + "[]"; + if (expected.length() < 1 || expected.length() > 2) { + result.fail(MessageFormat + .format("{0}: invalid expectation: expected array should contain either 1 or 2 elements but contains {1} elements", + arrayPrefix, expected.length())); + return; + } + if (!(expected.get(0) instanceof Number)) { + result.fail(MessageFormat + .format("{0}: invalid expectation: {1}expected array size ''{2}'' not a number", + arrayPrefix, (expected.length() == 1? "": "minimum "), expected.get(0))); + return; + } + if ((expected.length() == 2 && !(expected.get(1) instanceof Number))) { + result.fail(MessageFormat + .format("{0}: invalid expectation: maximum expected array size ''{1}'' not a number", + arrayPrefix, expected.get(1))); + return; + } + int minExpectedLength = expected.getInt(0); + if (minExpectedLength < 0) { + result.fail(MessageFormat + .format("{0}: invalid expectation: minimum expected array size ''{1}'' negative", + arrayPrefix, minExpectedLength)); + return; + } + int maxExpectedLength = expected.length() == 2 ? expected.getInt(1) + : minExpectedLength; + if (maxExpectedLength < minExpectedLength) { + result.fail(MessageFormat + .format("{0}: invalid expectation: maximum expected array size ''{1}'' less than minimum expected array size ''{2}''", + arrayPrefix, maxExpectedLength, minExpectedLength)); + return; + } + if (actual.length() < minExpectedLength + || actual.length() > maxExpectedLength) { + result.fail( + arrayPrefix, + MessageFormat.format( + "array size of {0}{1} elements", + minExpectedLength, + (expected.length() == 2 ? (" to " + maxExpectedLength) + : "")), + MessageFormat.format("{0} elements", + actual.length())); + } + } + +} diff --git a/src/main/java/org/skyscreamer/jsonassert/comparator/CustomComparator.java b/src/main/java/org/skyscreamer/jsonassert/comparator/CustomComparator.java index 7a9565ea..73337bdf 100644 --- a/src/main/java/org/skyscreamer/jsonassert/comparator/CustomComparator.java +++ b/src/main/java/org/skyscreamer/jsonassert/comparator/CustomComparator.java @@ -1,9 +1,23 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert.comparator; -import org.json.JSONException; import org.skyscreamer.jsonassert.Customization; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.JSONCompareResult; +import org.skyscreamer.jsonassert.ValueMatcherException; import java.util.Arrays; import java.util.Collection; @@ -18,11 +32,16 @@ public CustomComparator(JSONCompareMode mode, Customization... customizations) } @Override - public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException { + public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) { Customization customization = getCustomization(prefix); if (customization != null) { - if (!customization.matches(actualValue, expectedValue)) { - result.fail(prefix, expectedValue, actualValue); + try { + if (!customization.matches(prefix, actualValue, expectedValue, result)) { + result.fail(prefix, expectedValue, actualValue); + } + } + catch (ValueMatcherException e) { + result.fail(prefix, e); } } else { super.compareValues(prefix, expectedValue, actualValue, result); diff --git a/src/main/java/org/skyscreamer/jsonassert/comparator/DefaultComparator.java b/src/main/java/org/skyscreamer/jsonassert/comparator/DefaultComparator.java index d7ab30be..1e8efc01 100644 --- a/src/main/java/org/skyscreamer/jsonassert/comparator/DefaultComparator.java +++ b/src/main/java/org/skyscreamer/jsonassert/comparator/DefaultComparator.java @@ -1,23 +1,30 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert.comparator; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; -import org.skyscreamer.jsonassert.Customization; import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.JSONCompareResult; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - import static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.allJSONObjects; import static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.allSimpleValues; /** - * This class is the default json comparator implementation.

- * Comparison is performed according to {@link JSONCompareMode} that is passed as constructor's argument. + * This class is the default json comparator implementation. + * Comparison is performed according to {@link JSONCompareMode} that is passed as constructor's argument. */ public class DefaultComparator extends AbstractComparator { @@ -28,8 +35,7 @@ public DefaultComparator(JSONCompareMode mode) { } @Override - public void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) - throws JSONException { + public void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) { // Check that actual contains all the expected values checkJsonObjectKeysExpectedInActual(prefix, expected, actual, result); @@ -40,10 +46,14 @@ public void compareJSON(String prefix, JSONObject expected, JSONObject actual, J } @Override - public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) - throws JSONException { - if (expectedValue instanceof Number && actualValue instanceof Number) { - if (((Number)expectedValue).doubleValue() != ((Number)actualValue).doubleValue()) { + public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) { + if (expectedValue == actualValue) { + return; + } + if (expectedValue == null || actualValue == null) { + result.fail(prefix, expectedValue, actualValue); + } else if (areNumbers(expectedValue, actualValue)) { + if (areNotSameDoubles(expectedValue, actualValue)) { result.fail(prefix, expectedValue, actualValue); } } else if (expectedValue.getClass().isAssignableFrom(actualValue.getClass())) { @@ -60,8 +70,7 @@ public void compareValues(String prefix, Object expectedValue, Object actualValu } @Override - public void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) - throws JSONException { + public void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) { if (expected.length() != actual.length()) { result.fail(prefix + "[]: Expected " + expected.length() + " values but got " + actual.length()); return; @@ -80,4 +89,12 @@ public void compareJSONArray(String prefix, JSONArray expected, JSONArray actual recursivelyCompareJSONArray(prefix, expected, actual, result); } } + + protected boolean areNumbers(Object expectedValue, Object actualValue) { + return expectedValue instanceof Number && actualValue instanceof Number; + } + + protected boolean areNotSameDoubles(Object expectedValue, Object actualValue) { + return ((Number) expectedValue).doubleValue() != ((Number) actualValue).doubleValue(); + } } diff --git a/src/main/java/org/skyscreamer/jsonassert/comparator/JSONComparator.java b/src/main/java/org/skyscreamer/jsonassert/comparator/JSONComparator.java index b5b950fb..9a78d493 100644 --- a/src/main/java/org/skyscreamer/jsonassert/comparator/JSONComparator.java +++ b/src/main/java/org/skyscreamer/jsonassert/comparator/JSONComparator.java @@ -1,7 +1,20 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert.comparator; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; import org.skyscreamer.jsonassert.JSONCompareResult; @@ -13,13 +26,54 @@ */ public interface JSONComparator { - JSONCompareResult compareJSON(JSONObject expected, JSONObject actual) throws JSONException; + /** + * Compares two {@link JSONObject}s and returns the result of the comparison in a {@link JSONCompareResult} object. + * + * @param expected the expected JSON object + * @param actual the actual JSON object + * @return the result of the comparison + */ + JSONCompareResult compareJSON(JSONObject expected, JSONObject actual); - JSONCompareResult compareJSON(JSONArray expected, JSONArray actual) throws JSONException; + /** + * Compares two {@link JSONArray}s and returns the result of the comparison in a {@link JSONCompareResult} object. + * + * @param expected the expected JSON array + * @param actual the actual JSON array + * @return the result of the comparison + */ + JSONCompareResult compareJSON(JSONArray expected, JSONArray actual); - void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) throws JSONException; + /** + * Compares two {@link JSONObject}s on the provided path represented by {@code prefix} and + * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object. + * + * @param prefix the path in the json where the comparison happens + * @param expected the expected JSON object + * @param actual the actual JSON object + * @param result stores the actual state of the comparison result + */ + void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result); - void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException; + /** + * Compares two {@link Object}s on the provided path represented by {@code prefix} and + * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object. + * + * @param prefix the path in the json where the comparison happens + * @param expectedValue the expected value + * @param actualValue the actual value + * @param result stores the actual state of the comparison result + */ + void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result); - void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException; + /** + * Compares two {@link JSONArray}s on the provided path represented by {@code prefix} and + * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object. + * + * @param prefix the path in the json where the comparison happens + * @param expected the expected JSON array + * @param actual the actual JSON array + * @param result stores the actual state of the comparison result + */ + void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result); } diff --git a/src/main/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtil.java b/src/main/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtil.java index 6d28dd97..8a7fe18d 100644 --- a/src/main/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtil.java +++ b/src/main/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtil.java @@ -1,34 +1,69 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert.comparator; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; -import java.util.*; - /** - * Utility class that contains Json manipulation methods + * Utility class that contains Json manipulation methods. */ public final class JSONCompareUtil { private static Integer INTEGER_ONE = new Integer(1); - private JSONCompareUtil() {} + private JSONCompareUtil() { + } - public static Map arrayOfJsonObjectToMap(JSONArray array, String uniqueKey) throws JSONException { + /** + * Converts the provided {@link JSONArray} to a Map of {@link JSONObject}s where the key of each object + * is the value at {@code uniqueKey} in each object. + * + * @param array the JSON array to convert + * @param uniqueKey the key to map the JSON objects to + * @return the map of {@link JSONObject}s from {@code array} + */ + public static Map arrayOfJsonObjectToMap(JSONArray array, String uniqueKey) { Map valueMap = new HashMap(); - for(int i = 0 ; i < array.length() ; ++i) { - JSONObject jsonObject = (JSONObject)array.get(i); + for (int i = 0; i < array.length(); ++i) { + JSONObject jsonObject = (JSONObject) array.get(i); Object id = jsonObject.get(uniqueKey); valueMap.put(id, jsonObject); } return valueMap; } - - public static String findUniqueKey(JSONArray expected) throws JSONException { + /** + * Searches for the unique key of the {@code expected} JSON array. + * + * @param expected the array to find the unique key of + * @return the unique key if there's any, otherwise null + */ + public static String findUniqueKey(JSONArray expected) { // Find a unique key for the object (id, name, whatever) - JSONObject o = (JSONObject)expected.get(0); // There's at least one at this point - for(String candidate : getKeys(o)) { + JSONObject o = (JSONObject) expected.get(0); // There's at least one at this point + for (String candidate : getKeys(o)) { if (isUsableAsUniqueKey(candidate, expected)) return candidate; } // No usable unique key :-( @@ -36,12 +71,22 @@ public static String findUniqueKey(JSONArray expected) throws JSONException { } /** - * {@code candidate} is usable as a unique key if every element in the - * {@code array} is a JSONObject having that key, and no two values are the same. + *

Looks to see if candidate field is a possible unique key across a array of objects. + * Returns true IFF:

+ *
    + *
  1. array is an array of JSONObject + *
  2. candidate is a top-level field in each of of the objects in the array + *
  3. candidate is a simple value (not JSONObject or JSONArray) + *
  4. candidate is unique across all elements in the array + *
+ * + * @param candidate is usable as a unique key if every element in the + * @param array is a JSONObject having that key, and no two values are the same. + * @return true if the candidate can work as a unique id across array */ - public static boolean isUsableAsUniqueKey(String candidate, JSONArray array) throws JSONException { + public static boolean isUsableAsUniqueKey(String candidate, JSONArray array) { Set seenValues = new HashSet(); - for (int i = 0 ; i < array.length() ; i++) { + for (int i = 0; i < array.length(); i++) { Object item = array.get(i); if (item instanceof JSONObject) { JSONObject o = (JSONObject) item; @@ -62,29 +107,65 @@ public static boolean isUsableAsUniqueKey(String candidate, JSONArray array) thr return true; } - public static List jsonArrayToList(JSONArray expected) throws JSONException { + /** + * Converts the given {@link JSONArray} to a list of {@link Object}s. + * + * @param expected the JSON array to convert + * @return the list of objects from the {@code expected} array + */ + public static List jsonArrayToList(JSONArray expected) { List jsonObjects = new ArrayList(expected.length()); - for(int i = 0 ; i < expected.length() ; ++i) { - jsonObjects.add(expected.get(i)); + for (int i = 0; i < expected.length(); ++i) { + jsonObjects.add(getObjectOrNull(expected, i)); } return jsonObjects; } - public static boolean allSimpleValues(JSONArray array) throws JSONException { - for(int i = 0 ; i < array.length() ; ++i) { - if (!isSimpleValue(array.get(i))) { + /** + * Returns the value present in the given index position. If null value is present, it will return null + * + * @param jsonArray the JSON array to get value from + * @param index index of object to retrieve + * @return value at the given index position + */ + public static Object getObjectOrNull(JSONArray jsonArray, int index) { + return jsonArray.isNull(index) ? null : jsonArray.get(index); + } + + /** + * Returns whether all of the elements in the given array are simple values. + * + * @param array the JSON array to iterate through on + * @return true if all the elements in {@code array} are simple values + * @see #isSimpleValue(Object) + */ + public static boolean allSimpleValues(JSONArray array) { + for (int i = 0; i < array.length(); ++i) { + if (!array.isNull(i) && !isSimpleValue(array.get(i))) { return false; } } return true; } + /** + * Returns whether the given object is a simple value: not {@link JSONObject} and not {@link JSONArray}. + * + * @param o the object to inspect + * @return true if {@code o} is a simple value + */ public static boolean isSimpleValue(Object o) { return !(o instanceof JSONObject) && !(o instanceof JSONArray); } - public static boolean allJSONObjects(JSONArray array) throws JSONException { - for(int i = 0 ; i < array.length() ; ++i) { + /** + * Returns whether all elements in {@code array} are {@link JSONObject} instances. + * + * @param array the array to inspect + * @return true if all the elements in the given array are JSONObjects + */ + public static boolean allJSONObjects(JSONArray array) { + for (int i = 0; i < array.length(); ++i) { if (!(array.get(i) instanceof JSONObject)) { return false; } @@ -92,8 +173,14 @@ public static boolean allJSONObjects(JSONArray array) throws JSONException { return true; } - public static boolean allJSONArrays(JSONArray array) throws JSONException { - for(int i = 0 ; i < array.length() ; ++i) { + /** + * Returns whether all elements in {@code array} are {@link JSONArray} instances. + * + * @param array the array to inspect + * @return true if all the elements in the given array are JSONArrays + */ + public static boolean allJSONArrays(JSONArray array) { + for (int i = 0; i < array.length(); ++i) { if (!(array.get(i) instanceof JSONArray)) { return false; } @@ -101,11 +188,17 @@ public static boolean allJSONArrays(JSONArray array) throws JSONException { return true; } + /** + * Collects all keys in {@code jsonObject}. + * + * @param jsonObject the {@link JSONObject} to get the keys of + * @return the set of keys + */ public static Set getKeys(JSONObject jsonObject) { Set keys = new TreeSet(); Iterator iter = jsonObject.keys(); - while(iter.hasNext()) { - keys.add((String)iter.next()); + while (iter.hasNext()) { + keys.add((String) iter.next()); } return keys; } @@ -118,6 +211,13 @@ public static String formatUniqueKey(String key, String uniqueKey, Object value) return key + "[" + uniqueKey + "=" + value + "]"; } + /** + * Creates a cardinality map from {@code coll}. + * + * @param coll the collection of items to convert + * @param the type of elements in the input collection + * @return the cardinality map + */ public static Map getCardinalityMap(final Collection coll) { Map count = new HashMap(); for (T item : coll) { diff --git a/src/site/resources/cookbook.html b/src/site/resources/cookbook.html index 0ca0eb7a..2e271348 100644 --- a/src/site/resources/cookbook.html +++ b/src/site/resources/cookbook.html @@ -132,6 +132,6 @@

Cookbook

result, true); // Pass
- + diff --git a/src/site/resources/index.html b/src/site/resources/index.html index c694ef38..41f2c5ae 100644 --- a/src/site/resources/index.html +++ b/src/site/resources/index.html @@ -51,7 +51,7 @@

Introduction

  • JUnit
  • -


    The current version of JSONassert is 1.2.2

    +


    The current version of JSONassert is 2.0-rc1

    Examples

    diff --git a/src/site/resources/quickstart.html b/src/site/resources/quickstart.html index ed765f52..371e3575 100644 --- a/src/site/resources/quickstart.html +++ b/src/site/resources/quickstart.html @@ -49,7 +49,7 @@

    Quick Start

    <dependency>
      <groupId>org.skyscreamer</groupId>
      <artifactId>jsonassert</artifactId>
    -   <version>1.2.2</version>
    +   <version>2.0-rc1</version>
    </dependency>
    @@ -75,8 +75,9 @@

    Quick Start

    It is recommended that you leave strictMode - off, so your will be tests less brittle. + off, so your tests will be less brittle. Turn it on if you need to enforce a particular order for arrays, or if you want to ensure that the actual JSON does not have any fields beyond what's expected.

    + diff --git a/src/test/java/org/skyscreamer/jsonassert/ArrayValueMatcherTest.java b/src/test/java/org/skyscreamer/jsonassert/ArrayValueMatcherTest.java new file mode 100644 index 00000000..fa815d3c --- /dev/null +++ b/src/test/java/org/skyscreamer/jsonassert/ArrayValueMatcherTest.java @@ -0,0 +1,283 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.skyscreamer.jsonassert; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.text.MessageFormat; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Test; +import org.skyscreamer.jsonassert.comparator.ArraySizeComparator; +import org.skyscreamer.jsonassert.comparator.CustomComparator; +import org.skyscreamer.jsonassert.comparator.DefaultComparator; +import org.skyscreamer.jsonassert.comparator.JSONComparator; + +/** + * Unit tests for ArrayValueMatcher + * + * @author Duncan Mackinder + * + */ +public class ArrayValueMatcherTest { + + private static final String ARRAY_OF_JSONOBJECTS = "{a:[{background:white,id:1,type:row},{background:grey,id:2,type:row},{background:white,id:3,type:row},{background:grey,id:4,type:row}]}"; + private static final String ARRAY_OF_INTEGERS = "{a:[1,2,3,4,5]}"; + private static final String ARRAY_OF_JSONARRAYS = "{a:[[6,7,8],[9,10,11],[12,13,14],[19,20,21,22]]}"; + private static final JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); + + private void doTest(String jsonPath, ArrayValueMatcher arrayValueMatcher, String expectedJSON, + String actualJSON) { + Customization customization = new Customization(jsonPath, arrayValueMatcher); + JSONAssert.assertEquals(expectedJSON, actualJSON, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } + + private void doFailingMatchTest(String jsonPath, ArrayValueMatcher arrayValueMatcher, String expectedJSON, String actualJSON, String expectedMessagePattern) { + try { + doTest(jsonPath, arrayValueMatcher, expectedJSON, actualJSON); + } + catch (AssertionError e) { + String failureMessage = MessageFormat.format("Exception message ''{0}'', does not match expected pattern ''{1}''", e.getMessage(), expectedMessagePattern); + assertTrue(failureMessage, e.getMessage().matches(expectedMessagePattern)); + return; + } + fail("AssertionError not thrown"); + } + + @Test + public void matchesSecondElementOfJSONObjectArray() { + doTest("a", new ArrayValueMatcher(comparator, 1), "{a:[{background:grey,id:2,type:row}]}", ARRAY_OF_JSONOBJECTS); + } + + @Test + public void failsWhenSecondElementOfJSONObjectArrayDoesNotMatch() { + doFailingMatchTest("a", + new ArrayValueMatcher(comparator, 1), + "{a:[{background:DOES_NOT_MATCH,id:2,type:row}]}", + ARRAY_OF_JSONOBJECTS, + "a\\[1\\]\\.background\\s*Expected:\\s*DOES_NOT_MATCH\\s*got:\\s*grey\\s*"); + } + + @Test + public void failsWhenThirdElementOfJSONObjectArrayDoesNotMatchInMultiplePlaces() { + doFailingMatchTest("a", + new ArrayValueMatcher(comparator, 2), + "{a:[{background:DOES_NOT_MATCH,id:3,type:WRONG_TYPE}]}", + ARRAY_OF_JSONOBJECTS, + "a\\[2\\]\\.background\\s*Expected:\\s*DOES_NOT_MATCH\\s*got:\\s*white\\s*;\\s*a\\[2\\]\\.type\\s*Expected:\\s*WRONG_TYPE\\s*got:\\s*row\\s*"); + } + + @Test + public void failsWhenTwoElementsOfJSONObjectArrayDoNotMatch() { + doFailingMatchTest("a", + new ArrayValueMatcher(comparator, 1, 2), + "{a:[{background:DOES_NOT_MATCH,id:2,type:row},{background:white,id:3,type:WRONG_TYPE}]}", + ARRAY_OF_JSONOBJECTS, + "a\\[1\\]\\.background\\s*Expected:\\s*DOES_NOT_MATCH\\s*got:\\s*grey\\s*;\\s*a\\[2\\]\\.type\\s*Expected:\\s*WRONG_TYPE\\s*got:\\s*row\\s*"); + } + + @Test + public void matchesThirdElementOfSimpleValueArray() { + doTest("a", new ArrayValueMatcher(comparator, 2), "{a:[3]}", ARRAY_OF_INTEGERS); + } + + @Test + public void failsWhenTwoElementOfSimpleValueArrayDoNotMatch() { + doFailingMatchTest("a", new ArrayValueMatcher(comparator, 3, 4), "{a:[3,4]}", ARRAY_OF_INTEGERS, + "a\\[3\\]\\s*Expected:\\s3\\s*got:\\s*4\\s*;\\s*a\\[4\\]\\s*Expected:\\s*4\\s*got:\\s*5\\s*"); + } + + @Test + public void matchesFirstElementOfArrayOfJSONArrays() { + doTest("a", new ArrayValueMatcher(comparator, 0), "{a:[[6,7,8]]}", ARRAY_OF_JSONARRAYS); + } + + @Test + public void matchesSizeOfFirstThreeInnerArrays() { + JSONComparator innerArraySizeComparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER); + doTest("a", new ArrayValueMatcher(innerArraySizeComparator, 0, 2), "{a:[[3]]}", ARRAY_OF_JSONARRAYS); + } + + @Test + public void failsWhenInnerArraySizeDoesNotMatch() { + JSONComparator innerArraySizeComparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER); + doFailingMatchTest("a", + new ArrayValueMatcher(innerArraySizeComparator), + "{a:[[3]]}", + ARRAY_OF_JSONARRAYS, + "a\\[3\\]\\[\\]\\s*Expected:\\s*array size of 3 elements\\s*got:\\s*4 elements\\s*"); + } + + @Test + public void failsWhenInnerJSONObjectArrayElementDoesNotMatch() { + ArrayValueMatcher innerArrayValueMatcher = new ArrayValueMatcher(comparator, 1); + JSONComparator innerArrayComparator = new CustomComparator( + JSONCompareMode.LENIENT, new Customization("a[2]", innerArrayValueMatcher)); + doFailingMatchTest("a", + new ArrayValueMatcher(innerArrayComparator, 2), // tests inner array i.e. [12,13,14] + "{a:[[99]]}", + ARRAY_OF_JSONARRAYS, + "a\\[2\\]\\[1\\]\\s*Expected:\\s*99\\s*got:\\s*13\\s*"); + } + + @Test + public void matchesEveryElementOfJSONObjectArray() { + doTest("a", new ArrayValueMatcher(comparator), "{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS); + } + + @Test + public void failsWhenNotEveryElementOfJSONObjectArrayMatches() { + doFailingMatchTest("a", + new ArrayValueMatcher(comparator), + "{a:[{background:white}]}", + ARRAY_OF_JSONOBJECTS, + "a\\[1\\]\\.background\\s*Expected:\\s*white\\s*got:\\s*grey\\s*;\\s*a\\[3\\]\\.background\\s*Expected:\\s*white\\s*got:\\s*grey\\s*"); + } + + @Test + public void matchesEveryElementOfJSONObjectArrayWhenRangeTooLarge() { + doTest("a", new ArrayValueMatcher(comparator, 0, 500), "{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS); + } + + @Test + public void matchesElementPairsStartingFromElement1OfJSONObjectArrayWhenRangeTooLarge() { + doTest("a", new ArrayValueMatcher(comparator, 1, 500), "{a:[{background:grey},{background:white}]}", ARRAY_OF_JSONOBJECTS); + } + + @Test + public void matchesElementPairsStartingFromElement0OfJSONObjectArrayWhenRangeTooLarge() { + doTest("a", new ArrayValueMatcher(comparator), "{a:[{background:white},{background:grey}]}", ARRAY_OF_JSONOBJECTS); + } + + @Test + public void failsWhenAppliedToNonArray() { + try { + doTest("a", new ArrayValueMatcher(comparator), "{a:[{background:white}]}", "{a:{attr1:value1,attr2:value2}}"); + } + catch (IllegalArgumentException e) { + assertEquals("Exception message", "ArrayValueMatcher applied to non-array actual value", e.getMessage()); + return; + } + fail("Did not throw IllegalArgumentException"); + } + + /* + * Following tests verify the ability to match an element containing either + * a simple value or a JSON object against simple value or JSON object + * without requiring expected value to be wrapped in an array reducing + * slightly the syntactic load on teh test author & reader. + */ + + @Test + public void simpleValueMatchesSecondElementOfJSONObjectArray() { + doTest("a", new ArrayValueMatcher(comparator, 3), "{a:4}", ARRAY_OF_INTEGERS); + } + + @Test + public void jsonObjectMatchesSecondElementOfJSONObjectArray() { + doTest("a", new ArrayValueMatcher(comparator, 1), "{a:{background:grey,id:2,type:row}}", ARRAY_OF_JSONOBJECTS); + } + + /* + * Following tests contain copies of code quoted in ArrayValueMatcher JavaDoc and are included to verify that the exact code documented works as expected. + */ + @Test + public void verifyIdAttributeOfFirstArrayElementMatches() { + JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); + Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0)); + JSONAssert.assertEquals("{a:[{id:1}]}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } + + @Test + public void verifyIdAttributeOfFirstArrayElementMatchesSimplifiedExpectedSyntax() { + JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); + Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0)); + JSONAssert.assertEquals("{a:{id:1}}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } + + @Test + public void verifyTypeAttributeOfSecondAndThirdElementMatchesRow() { + JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); + Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1, 2)); + JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } + + @Test + public void verifyTypeAttributeOfEveryArrayElementMatchesRow() { + JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); + Customization customization = new Customization("a", new ArrayValueMatcher(comparator)); + JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } + + @Test + public void verifyEveryArrayElementWithCustomComparator() { + // get length of array we will verify + int aLength = ((JSONArray)((JSONObject)JSONParser.parseJSON(ARRAY_OF_JSONOBJECTS)).get("a")).length(); + // create array of customizations one for each array element + RegularExpressionValueMatcher regExValueMatcher = new RegularExpressionValueMatcher("\\d+"); // matches one or more digits + Customization[] customizations = new Customization[aLength]; + for (int i=0; i regExArrayValueMatcher = new ArrayValueMatcher(regExComparator); + Customization regExArrayValueCustomization = new Customization("a", regExArrayValueMatcher); + CustomComparator regExCustomArrayValueComparator = new CustomComparator(JSONCompareMode.STRICT_ORDER, new Customization[] { regExArrayValueCustomization }); + JSONAssert.assertEquals("{a:[{id:X}]}", ARRAY_OF_JSONOBJECTS, regExCustomArrayValueComparator); + } + + @Test + public void verifyBackgroundAttributesOfEveryArrayElementAlternateBetweenWhiteAndGrey() { + JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); + Customization customization = new Customization("a", new ArrayValueMatcher(comparator)); + JSONAssert.assertEquals("{a:[{background:white},{background:grey}]}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } + + @Test + public void verifyEveryElementOfArrayIsJSONArrayOfLength3() { + JSONComparator comparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER); + Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0, 2)); + JSONAssert.assertEquals("{a:[[3]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } + + @Test + public void verifySecondElementOfArrayIsJSONArrayWhoseFirstElementIs9() { + Customization innerCustomization = new Customization("a[1]", new ArrayValueMatcher(comparator, 0)); + JSONComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, innerCustomization); + Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1)); + JSONAssert.assertEquals("{a:[[9]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } + + @Test + public void verifySecondElementOfArrayIsJSONArrayWhoseFirstElementIs9WithSimpliedExpectedString() { + Customization innerCustomization = new Customization("a[1]", new ArrayValueMatcher(comparator, 0)); + JSONComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, innerCustomization); + Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1)); + JSONAssert.assertEquals("{a:[9]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } + + @Test + public void verifySecondElementOfArrayIsJSONArrayWhoseFirstElementIs9WithEvenMoreSimpliedExpectedString() { + Customization innerCustomization = new Customization("a[1]", new ArrayValueMatcher(comparator, 0)); + JSONComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, innerCustomization); + Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1)); + JSONAssert.assertEquals("{a:9}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization)); + } +} diff --git a/src/test/java/org/skyscreamer/jsonassert/DependencyTest.java b/src/test/java/org/skyscreamer/jsonassert/DependencyTest.java index fe449060..ad0519da 100644 --- a/src/test/java/org/skyscreamer/jsonassert/DependencyTest.java +++ b/src/test/java/org/skyscreamer/jsonassert/DependencyTest.java @@ -1,3 +1,17 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; import org.json.JSONObject; diff --git a/src/test/java/org/skyscreamer/jsonassert/JSONArrayWithNullTest.java b/src/test/java/org/skyscreamer/jsonassert/JSONArrayWithNullTest.java new file mode 100644 index 00000000..0bf11531 --- /dev/null +++ b/src/test/java/org/skyscreamer/jsonassert/JSONArrayWithNullTest.java @@ -0,0 +1,48 @@ +package org.skyscreamer.jsonassert; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.junit.Test; + +public class JSONArrayWithNullTest { + @Test + public void testJSONArrayWithNullValue() { + JSONArray jsonArray1 = getJSONArray1(); + JSONArray jsonArray2 = getJSONArray2(); + + JSONAssert.assertEquals(jsonArray1, jsonArray2, true); + JSONAssert.assertEquals(jsonArray1, jsonArray2, false); + } + + @Test + public void testJSONArrayWithNullValueAndJsonObject() { + JSONArray jsonArray1 = getJSONArray1(); + JSONObject jsonObject1 = new JSONObject(); + jsonObject1.put("hey", "value"); + + JSONArray jsonArray2 = getJSONArray2(); + JSONObject jsonObject2 = new JSONObject(); + jsonObject2.put("hey", "value"); + + JSONAssert.assertEquals(jsonArray1, jsonArray2, true); + JSONAssert.assertEquals(jsonArray1, jsonArray2, false); + } + + private JSONArray getJSONArray1() { + JSONArray jsonArray1 = new JSONArray(); + jsonArray1.put(1); + jsonArray1.put(JSONObject.NULL); + jsonArray1.put(3); + jsonArray1.put(2); + return jsonArray1; + } + + private JSONArray getJSONArray2() { + JSONArray jsonArray1 = new JSONArray(); + jsonArray1.put(1); + jsonArray1.put(JSONObject.NULL); + jsonArray1.put(3); + jsonArray1.put(2); + return jsonArray1; + } +} diff --git a/src/test/java/org/skyscreamer/jsonassert/JSONAssertTest.java b/src/test/java/org/skyscreamer/jsonassert/JSONAssertTest.java index 65014b68..e17f684b 100644 --- a/src/test/java/org/skyscreamer/jsonassert/JSONAssertTest.java +++ b/src/test/java/org/skyscreamer/jsonassert/JSONAssertTest.java @@ -1,21 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.skyscreamer.jsonassert.JSONCompareMode.LENIENT; +import static org.skyscreamer.jsonassert.JSONCompareMode.NON_EXTENSIBLE; +import static org.skyscreamer.jsonassert.JSONCompareMode.STRICT; +import static org.skyscreamer.jsonassert.JSONCompareMode.STRICT_ORDER; + import java.util.Arrays; import org.json.JSONArray; -import org.json.JSONException; import org.json.JSONObject; import org.junit.Assert; import org.junit.Test; - -import static org.skyscreamer.jsonassert.JSONCompareMode.*; +import org.skyscreamer.jsonassert.comparator.CustomComparator; +import org.skyscreamer.jsonassert.comparator.JSONComparator; /** * Unit tests for {@link JSONAssert} */ public class JSONAssertTest { @Test - public void testString() throws JSONException { + public void testString() { testPass("\"Joe\"", "\"Joe\"", STRICT); testPass("\"Joe\"", "\"Joe\"", LENIENT); testPass("\"Joe\"", "\"Joe\"", NON_EXTENSIBLE); @@ -27,7 +47,7 @@ public void testString() throws JSONException { } @Test - public void testNumber() throws JSONException { + public void testNumber() { testPass("123", "123", STRICT); testPass("123", "123", LENIENT); testPass("123", "123", NON_EXTENSIBLE); @@ -45,7 +65,7 @@ public void testNumber() throws JSONException { } @Test - public void testSimple() throws JSONException { + public void testSimple() { testPass("{id:1}", "{id:1}", STRICT); testFail("{id:1}", "{id:2}", STRICT); testPass("{id:1}", "{id:1}", LENIENT); @@ -57,7 +77,7 @@ public void testSimple() throws JSONException { } @Test - public void testSimpleStrict() throws JSONException { + public void testSimpleStrict() { testPass("{id:1}", "{id:1,name:\"Joe\"}", LENIENT); testFail("{id:1}", "{id:1,name:\"Joe\"}", STRICT); testPass("{id:1}", "{id:1,name:\"Joe\"}", STRICT_ORDER); @@ -65,7 +85,7 @@ public void testSimpleStrict() throws JSONException { } @Test - public void testReversed() throws JSONException { + public void testReversed() { testPass("{name:\"Joe\",id:1}", "{id:1,name:\"Joe\"}", LENIENT); testPass("{name:\"Joe\",id:1}", "{id:1,name:\"Joe\"}", STRICT); testPass("{name:\"Joe\",id:1}", "{id:1,name:\"Joe\"}", NON_EXTENSIBLE); @@ -73,7 +93,7 @@ public void testReversed() throws JSONException { } @Test // Currently JSONAssert assumes JSONObject. - public void testArray() throws JSONException { + public void testArray() { testPass("[1,2,3]","[1,2,3]", STRICT); testPass("[1,2,3]","[1,3,2]", LENIENT); testFail("[1,2,3]","[1,3,2]", STRICT); @@ -85,7 +105,7 @@ public void testArray() throws JSONException { } @Test - public void testNested() throws JSONException { + public void testNested() { testPass("{id:1,address:{addr1:\"123 Main\", addr2:null, city:\"Houston\", state:\"TX\"}}", "{id:1,address:{addr1:\"123 Main\", addr2:null, city:\"Houston\", state:\"TX\"}}", STRICT); testFail("{id:1,address:{addr1:\"123 Main\", addr2:null, city:\"Houston\", state:\"TX\"}}", @@ -93,7 +113,7 @@ public void testNested() throws JSONException { } @Test - public void testVeryNested() throws JSONException { + public void testVeryNested() { testPass("{a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{k:{l:{m:{n:{o:{p:\"blah\"}}}}}}}}}}}}}}}}", "{a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{k:{l:{m:{n:{o:{p:\"blah\"}}}}}}}}}}}}}}}}", STRICT); testFail("{a:{b:{c:{d:{e:{f:{g:{h:{i:{j:{k:{l:{m:{n:{o:{p:\"blah\"}}}}}}}}}}}}}}}}", @@ -101,7 +121,7 @@ public void testVeryNested() throws JSONException { } @Test - public void testSimpleArray() throws JSONException { + public void testSimpleArray() { testPass("{id:1,pets:[\"dog\",\"cat\",\"fish\"]}", // Exact to exact (strict) "{id:1,pets:[\"dog\",\"cat\",\"fish\"]}", STRICT); @@ -132,40 +152,40 @@ public void testSimpleArray() throws JSONException { } @Test - public void testSimpleMixedArray() throws JSONException { + public void testSimpleMixedArray() { testPass("{stuff:[321, \"abc\"]}", "{stuff:[\"abc\", 321]}", LENIENT); testFail("{stuff:[321, \"abc\"]}", "{stuff:[\"abc\", 789]}", LENIENT); } @Test - public void testComplexMixedStrictArray() throws JSONException { + public void testComplexMixedStrictArray() { testPass("{stuff:[{pet:\"cat\"},{car:\"Ford\"}]}", "{stuff:[{pet:\"cat\"},{car:\"Ford\"}]}", STRICT); } @Test - public void testComplexMixedArray() throws JSONException { + public void testComplexMixedArray() { testPass("{stuff:[{pet:\"cat\"},{car:\"Ford\"}]}", "{stuff:[{pet:\"cat\"},{car:\"Ford\"}]}", LENIENT); } @Test - public void testComplexArrayNoUniqueID() throws JSONException { + public void testComplexArrayNoUniqueID() { testPass("{stuff:[{address:{addr1:\"123 Main\"}}, {address:{addr1:\"234 Broad\"}}]}", "{stuff:[{address:{addr1:\"123 Main\"}}, {address:{addr1:\"234 Broad\"}}]}", LENIENT); } @Test - public void testSimpleAndComplexStrictArray() throws JSONException { + public void testSimpleAndComplexStrictArray() { testPass("{stuff:[123,{a:\"b\"}]}", "{stuff:[123,{a:\"b\"}]}", STRICT); } @Test - public void testSimpleAndComplexArray() throws JSONException { + public void testSimpleAndComplexArray() { testPass("{stuff:[123,{a:\"b\"}]}", "{stuff:[123,{a:\"b\"}]}", LENIENT); } @Test - public void testComplexArray() throws JSONException { + public void testComplexArray() { testPass("{id:1,name:\"Joe\",friends:[{id:2,name:\"Pat\",pets:[\"dog\"]},{id:3,name:\"Sue\",pets:[\"bird\",\"fish\"]}],pets:[]}", "{id:1,name:\"Joe\",friends:[{id:2,name:\"Pat\",pets:[\"dog\"]},{id:3,name:\"Sue\",pets:[\"bird\",\"fish\"]}],pets:[]}", STRICT); // Exact to exact (strict) @@ -196,23 +216,23 @@ public void testComplexArray() throws JSONException { } @Test - public void testArrayOfArraysStrict() throws JSONException { + public void testArrayOfArraysStrict() { testPass("{id:1,stuff:[[1,2],[2,3],[],[3,4]]}", "{id:1,stuff:[[1,2],[2,3],[],[3,4]]}", STRICT); testFail("{id:1,stuff:[[1,2],[2,3],[3,4],[]]}", "{id:1,stuff:[[1,2],[2,3],[],[3,4]]}", STRICT); } @Test - public void testArrayOfArrays() throws JSONException { + public void testArrayOfArrays() { testPass("{id:1,stuff:[[4,3],[3,2],[],[1,2]]}", "{id:1,stuff:[[1,2],[2,3],[],[3,4]]}", LENIENT); } @Test - public void testLenientArrayRecursion() throws JSONException { + public void testLenientArrayRecursion() { testPass("[{\"arr\":[5, 2, 1]}]", "[{\"b\":3, \"arr\":[1, 5, 2]}]", LENIENT); } @Test - public void testFieldMismatch() throws JSONException { + public void testFieldMismatch() { JSONCompareResult result = JSONCompare.compareJSON("{name:\"Pat\"}", "{name:\"Sue\"}", STRICT); FieldComparisonFailure comparisonFailure = result.getFieldFailures().iterator().next(); Assert.assertEquals("Pat", comparisonFailure.getExpected()); @@ -221,7 +241,7 @@ public void testFieldMismatch() throws JSONException { } @Test - public void testBooleanArray() throws JSONException { + public void testBooleanArray() { testPass("[true, false, true, true, false]", "[true, false, true, true, false]", STRICT); testPass("[false, true, true, false, true]", "[true, false, true, true, false]", LENIENT); testFail("[false, true, true, false, true]", "[true, false, true, true, false]", STRICT); @@ -230,127 +250,136 @@ public void testBooleanArray() throws JSONException { } @Test - public void testNullProperty() throws JSONException { + public void testNullProperty() { testFail("{id:1,name:\"Joe\"}", "{id:1,name:null}", STRICT); testFail("{id:1,name:null}", "{id:1,name:\"Joe\"}", STRICT); } @Test - public void testIncorrectTypes() throws JSONException { + public void testIncorrectTypes() { testFail("{id:1,name:\"Joe\"}", "{id:1,name:[]}", STRICT); testFail("{id:1,name:[]}", "{id:1,name:\"Joe\"}", STRICT); } @Test - public void testNullEquality() throws JSONException { + public void testNullEquality() { testPass("{id:1,name:null}", "{id:1,name:null}", STRICT); } @Test - public void testExpectedArrayButActualObject() throws JSONException { + public void testExpectedArrayButActualObject() { testFail("[1]", "{id:1}", LENIENT); } @Test - public void testExpectedObjectButActualArray() throws JSONException { + public void testExpectedObjectButActualArray() { testFail("{id:1}", "[1]", LENIENT); } @Test - public void testEquivalentIntAndLong() throws JSONException { + public void testEquivalentIntAndLong() { JSONObject expected = new JSONObject(); JSONObject actual = new JSONObject(); - expected.put("id", new Integer(12345)); - actual.put("id", new Long(12345)); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Long.valueOf(12345)); JSONAssert.assertEquals(expected, actual, true); JSONAssert.assertEquals(actual, expected, true); } @Test - public void testEquivalentIntAndDouble() throws JSONException { + public void testEquivalentIntAndDouble() { JSONObject expected = new JSONObject(); JSONObject actual = new JSONObject(); - expected.put("id", new Integer(12345)); - actual.put("id", new Double(12345.0)); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12345.0)); JSONAssert.assertEquals(expected, actual, true); JSONAssert.assertEquals(actual, expected, true); } @Test(expected = AssertionError.class) - public void testAssertNotEqualsWhenEqualStrict() throws JSONException { + public void testAssertNotEqualsWhenEqualStrict() { JSONObject expected = new JSONObject(); JSONObject actual = new JSONObject(); - expected.put("id", new Integer(12345)); - actual.put("id", new Double(12345)); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12345)); JSONAssert.assertNotEquals(expected, actual, true); } @Test(expected = AssertionError.class) - public void testAssertNotEqualsWhenEqualLenient() throws JSONException { + public void testAssertNotEqualsWhenEqualLenient() { JSONObject expected = new JSONObject(); JSONObject actual = new JSONObject(); - expected.put("id", new Integer(12345)); - actual.put("id", new Double(12345)); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12345)); JSONAssert.assertNotEquals(expected, actual, false); } @Test() - public void testAssertNotEqualsWhenEqualDiffObjectsStrict() throws JSONException { + public void testAssertNotEqualsWhenEqualDiffObjectsStrict() { JSONObject expected = new JSONObject(); JSONObject actual = new JSONObject(); - expected.put("id", new Integer(12345)); + expected.put("id", Integer.valueOf(12345)); expected.put("name", "Joe"); - actual.put("id", new Double(12345)); + actual.put("id", Double.valueOf(12345)); JSONAssert.assertNotEquals(expected, actual, true); } @Test(expected = AssertionError.class) - public void testAssertNotEqualsWhenEqualDiffObjectsLenient() throws JSONException { + public void testAssertNotEqualsWhenEqualDiffObjectsLenient() { JSONObject expected = new JSONObject(); JSONObject actual = new JSONObject(); - expected.put("id", new Integer(12345)); + expected.put("id", Integer.valueOf(12345)); expected.put("name", "Joe"); actual.put("name", "Joe"); - actual.put("id", new Double(12345)); + actual.put("id", Double.valueOf(12345)); JSONAssert.assertNotEquals(expected, actual, false); } @Test() - public void testAssertNotEqualsWhenDifferentStrict() throws JSONException { + public void testAssertNotEqualsWhenDifferentStrict() { JSONObject expected = new JSONObject(); JSONObject actual = new JSONObject(); - expected.put("id", new Integer(12345)); - actual.put("id", new Double(12346)); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12346)); JSONAssert.assertNotEquals(expected, actual, true); } @Test() - public void testAssertNotEqualsWhenDifferentLenient() throws JSONException { + public void testAssertNotEqualsWhenDifferentLenient() { JSONObject expected = new JSONObject(); JSONObject actual = new JSONObject(); - expected.put("id", new Integer(12345)); - actual.put("id", new Double(12346)); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12346)); JSONAssert.assertNotEquals(expected, actual, false); } @Test() - public void testAssertNotEqualsString() throws JSONException { + public void testAssertNotEqualsString() { JSONAssert.assertNotEquals("[1,2,3]", "[1,3,2]", STRICT); JSONAssert.assertNotEquals("[1,2,3]", "[1,2,4]", LENIENT); JSONAssert.assertNotEquals("[1,2,3]", "[1,3,2]", true); JSONAssert.assertNotEquals("[1,2,3]", "[1,2,4]", false); } + + @Test() + public void testAssertEqualsString() { + JSONAssert.assertEquals("[1,2,3]", "[1,2,3]", true); + JSONAssert.assertEquals("{id:12345}", "{id:12345}", false); + JSONAssert.assertEquals("{id:12345}", "{id:12345, name:\"john\"}", LENIENT); + JSONAssert.assertEquals("{id:12345}", "{id:12345}", LENIENT); + JSONAssert.assertEquals("{id:12345}", "{id:12345, name:\"john\"}", LENIENT); + } @Test() - public void testAssertNotEqualsStringAndJSONObject() throws JSONException { + public void testAssertNotEqualsStringAndJSONObject() { JSONObject actual = new JSONObject(); - actual.put("id", new Double(12345)); + actual.put("id", Double.valueOf(12345)); JSONAssert.assertEquals("{id:12345}", actual, false); JSONAssert.assertNotEquals("{id:12346}", actual, false); } @Test() - public void testAssertNotEqualsJSONArray() throws JSONException { + public void testAssertNotEqualsJSONArray() { JSONArray actual = new JSONArray(Arrays.asList(1, 2, 3)); JSONAssert.assertEquals("[1,2,3]", actual, false); JSONAssert.assertNotEquals("[1,2,4]", actual, false); @@ -358,9 +387,260 @@ public void testAssertNotEqualsJSONArray() throws JSONException { JSONAssert.assertNotEquals(new JSONArray(Arrays.asList(1, 2, 4)), actual, false); JSONAssert.assertNotEquals(new JSONArray(Arrays.asList(1, 3, 2)), actual, true); } + + @Test + public void testAssertEqualsStringJSONArrayBooleanWithMessage() { + JSONArray actual = new JSONArray(Arrays.asList(1, 2, 3)); + JSONAssert.assertEquals("Message", "[1,2,3]", actual, false); + performAssertEqualsTestForMessageVerification("[1,2,4]", actual, false); + performAssertEqualsTestForMessageVerification("[1,3,2]", actual, true); + } + + @Test + public void testAssertEqualsStringJSONArrayCompareModeWithMessage() { + JSONArray actual = new JSONArray(Arrays.asList(1, 2, 3)); + JSONAssert.assertEquals("Message", "[1,2,3]", actual, LENIENT); + performAssertEqualsTestForMessageVerification("[1,2,4]", actual, LENIENT); + performAssertEqualsTestForMessageVerification("[1,3,2]", actual, STRICT); + } + @Test + public void testAssertEqualsJSONArray2BooleanWithMessage() { + JSONArray actual = new JSONArray(Arrays.asList(1, 2, 3)); + JSONAssert.assertEquals("Message", new JSONArray(Arrays.asList(1, 2, 3)), actual, false); + performAssertEqualsTestForMessageVerification(new JSONArray(Arrays.asList(1, 2, 4)), actual, false); + performAssertEqualsTestForMessageVerification(new JSONArray(Arrays.asList(1, 3, 2)), actual, true); + } + + @Test + public void testAssertEqualsJSONArray2JSONCompareWithMessage() { + JSONArray actual = new JSONArray(Arrays.asList(1, 2, 3)); + + JSONAssert.assertEquals("Message", new JSONArray(Arrays.asList(1, 2, 3)), actual, LENIENT); + performAssertEqualsTestForMessageVerification(new JSONArray(Arrays.asList(1, 2, 4)), actual, LENIENT); + performAssertEqualsTestForMessageVerification(new JSONArray(Arrays.asList(1, 3, 2)), actual, STRICT); + } + + @Test + public void testAssertEqualsString2Boolean() { + JSONAssert.assertEquals("Message", "{id:12345}", "{id:12345}", false); + JSONAssert.assertEquals("Message", "{id:12345}", "{id:12345, name:\"john\"}", false); + + performAssertEqualsTestForMessageVerification("{id:12345}", "{id:12345, name:\"john\"}", true); + performAssertEqualsTestForMessageVerification("{id:12345}", "{id:123456}", false); + } + + @Test + public void testAssertEqualsString2JSONCompare() { + JSONAssert.assertEquals("Message", "{id:12345}", "{id:12345}", LENIENT); + JSONAssert.assertEquals("Message", "{id:12345}", "{id:12345, name:\"john\"}", LENIENT); + + performAssertEqualsTestForMessageVerification("{id:12345}", "{id:12345, name:\"john\"}", STRICT); + performAssertEqualsTestForMessageVerification("{id:12345}", "{id:123456}", LENIENT); + } + + @Test + public void testAssertEqualsStringJSONObjectBoolean() { + JSONObject actual = new JSONObject(); + actual.put("id", Double.valueOf(12345)); + JSONAssert.assertEquals("Message", "{id:12345}", actual, false); + performAssertEqualsTestForMessageVerification("{id:12346}", actual, false); + performAssertEqualsTestForMessageVerification("[1,2,3]", "[1,3,2]", true); + } + + @Test + public void testAssertEqualsStringJSONObjectJSONCompare() { + JSONObject actual = new JSONObject(); + actual.put("id", Double.valueOf(12345)); + JSONAssert.assertEquals("Message", "{id:12345}", actual, LENIENT); + performAssertEqualsTestForMessageVerification("{id:12346}", actual, LENIENT); + performAssertEqualsTestForMessageVerification("[1,2,3]", "[1,3,2]", STRICT); + } + + @Test + public void testAssertEqualsJSONObject2JSONCompare() { + JSONObject expected = new JSONObject(); + JSONObject actual = new JSONObject(); + expected.put("id", Integer.valueOf(12345)); + actual.put("name", "Joe"); + actual.put("id", Integer.valueOf(12345)); + JSONAssert.assertEquals("Message", expected, actual, LENIENT); + + expected.put("street", "St. Paul"); + performAssertEqualsTestForMessageVerification(expected, actual, LENIENT); + + expected = new JSONObject(); + actual = new JSONObject(); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12346)); + performAssertEqualsTestForMessageVerification(expected, actual, STRICT); + } + + @Test + public void testAssertEqualsJSONObject2Boolean() { + JSONObject expected = new JSONObject(); + JSONObject actual = new JSONObject(); + expected.put("id", Integer.valueOf(12345)); + actual.put("name", "Joe"); + actual.put("id", Integer.valueOf(12345)); + JSONAssert.assertEquals("Message", expected, actual, false); + + expected.put("street", "St. Paul"); + performAssertEqualsTestForMessageVerification(expected, actual, false); + + expected = new JSONObject(); + actual = new JSONObject(); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12346)); + performAssertEqualsTestForMessageVerification(expected, actual, true); + } + + @Test + public void testAssertEqualsString2JsonComparator() throws IllegalArgumentException { + JSONAssert.assertEquals("Message", "{\"entry\":{\"id\":x}}", "{\"entry\":{\"id\":1}}", + new CustomComparator( + JSONCompareMode.STRICT, + new Customization("entry.id", + new RegularExpressionValueMatcher("\\d")) + )); + + performAssertEqualsTestForMessageVerification("{\"entry\":{\"id\":x}}", "{\"entry\":{\"id\":NOT_A_NUMBER}}", + new CustomComparator( + JSONCompareMode.STRICT, + new Customization("entry.id", + new RegularExpressionValueMatcher("\\d")) + )); + } + + @Test + public void testAssertNotEqualsStringJSONArrayBooleanWithMessage() { + JSONArray actual = new JSONArray(Arrays.asList(1, 2, 3)); + JSONAssert.assertNotEquals("Message", "[1,4,3]", actual, false); + JSONAssert.assertNotEquals("Message", "[1,4,3]", actual, true); + performAssertNotEqualsTestForMessageVerification("[1,3,2]", actual, false); + performAssertNotEqualsTestForMessageVerification("[1,2,3]", actual, true); + } + + @Test + public void testAssertNotEqualsStringJSONArrayCompareModeWithMessage() { + JSONArray actual = new JSONArray(Arrays.asList(1, 2, 3)); + JSONAssert.assertNotEquals("Message", "[1,2,4]", actual, LENIENT); + JSONAssert.assertNotEquals("Message", "[1,2,4]", actual, STRICT); + performAssertNotEqualsTestForMessageVerification("[1,3,2]", actual, LENIENT); + performAssertNotEqualsTestForMessageVerification("[1,2,3]", actual, STRICT); + } + + @Test + public void testAssertNotEqualsJSONArray2BooleanWithMessage() { + JSONArray actual = new JSONArray(Arrays.asList(1, 2, 3)); + JSONAssert.assertNotEquals("Message", new JSONArray(Arrays.asList(1, 4, 3)), actual, false); + performAssertNotEqualsTestForMessageVerification(new JSONArray(Arrays.asList(1, 3, 2)), actual, false); + performAssertNotEqualsTestForMessageVerification(new JSONArray(Arrays.asList(1, 2, 3)), actual, true); + } + + @Test + public void testAssertNotEqualsJSONArray2JSONCompareWithMessage() { + JSONArray actual = new JSONArray(Arrays.asList(1, 2, 3)); + + JSONAssert.assertNotEquals("Message", new JSONArray(Arrays.asList(1, 4, 3)), actual, LENIENT); + performAssertNotEqualsTestForMessageVerification(new JSONArray(Arrays.asList(1, 3, 2)), actual, LENIENT); + performAssertNotEqualsTestForMessageVerification(new JSONArray(Arrays.asList(1, 2, 3)), actual, STRICT); + } + + @Test + public void testAssertNotEqualsString2Boolean() { + JSONAssert.assertNotEquals("Message", "{id:12345}", "{id:45}", false); + JSONAssert.assertNotEquals("Message", "{id:12345}", "{id:345, name:\"john\"}", false); + + performAssertNotEqualsTestForMessageVerification("{id:12345}", "{id:12345}", true); + performAssertNotEqualsTestForMessageVerification("{id:12345}", "{id:12345, name:\"John\"}", false); + } + + @Test + public void testAssertNotEqualsString2JSONCompare() { + JSONAssert.assertNotEquals("Message", "{id:12345}", "{id:123}", LENIENT); + JSONAssert.assertNotEquals("Message", "{id:12345, name:\"John\"}", "{id:12345}", LENIENT); + + performAssertNotEqualsTestForMessageVerification("{id:12345}", "{id:12345, name:\"john\"}", LENIENT); + performAssertNotEqualsTestForMessageVerification("{id:12345}", "{id:12345}", STRICT); + } + + @Test + public void testAssertNotEqualsStringJSONObjectBoolean() { + JSONObject actual = new JSONObject(); + actual.put("id", Double.valueOf(12345)); + JSONAssert.assertNotEquals("Message", "{id:1234}", actual, false); + performAssertNotEqualsTestForMessageVerification("{id:12345}", actual, false); + performAssertNotEqualsTestForMessageVerification("[1,2,3]", "[1,2,3]", true); + } + + @Test + public void testAssertNotEqualsStringJSONObjectJSONCompare() { + JSONObject actual = new JSONObject(); + actual.put("id", Double.valueOf(12345)); + JSONAssert.assertNotEquals("Message", "{id:1234}", actual, LENIENT); + performAssertNotEqualsTestForMessageVerification("{id:12345}", actual, LENIENT); + performAssertNotEqualsTestForMessageVerification("[1,2,3]", "[1,2,3]", STRICT); + } + + @Test + public void testAssertNtEqualsJSONObject2JSONCompare() { + JSONObject expected = new JSONObject(); + JSONObject actual = new JSONObject(); + expected.put("id", Integer.valueOf(12345)); + actual.put("name", "Joe"); + actual.put("id", Integer.valueOf(123)); + JSONAssert.assertNotEquals("Message", expected, actual, LENIENT); + + actual.remove("id"); + actual.put("id", Integer.valueOf(12345)); + performAssertNotEqualsTestForMessageVerification(expected, actual, LENIENT); + + expected = new JSONObject(); + actual = new JSONObject(); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12345)); + performAssertNotEqualsTestForMessageVerification(expected, actual, STRICT); + } + + @Test + public void testAssertNotEqualsJSONObject2Boolean() { + JSONObject expected = new JSONObject(); + JSONObject actual = new JSONObject(); + expected.put("id", Integer.valueOf(12345)); + actual.put("name", "Joe"); + actual.put("id", Integer.valueOf(123)); + JSONAssert.assertNotEquals("Message", expected, actual, false); + + actual.remove("id"); + actual.put("id", Integer.valueOf(12345)); + performAssertNotEqualsTestForMessageVerification(expected, actual, false); + + expected = new JSONObject(); + actual = new JSONObject(); + expected.put("id", Integer.valueOf(12345)); + actual.put("id", Double.valueOf(12345)); + performAssertNotEqualsTestForMessageVerification(expected, actual, true); + } + + @Test + public void testAssertNotEqualsString2JsonComparator() throws IllegalArgumentException { + JSONAssert.assertNotEquals("Message", "{\"entry\":{\"id\":x}}", "{\"entry\":{\"id\":NOT_A_NUMBER}}", + new CustomComparator( + JSONCompareMode.STRICT, + new Customization("entry.id", + new RegularExpressionValueMatcher("\\d")) + )); + + performAssertNotEqualsTestForMessageVerification("{\"entry\":{\"id\":x}}", "{\"entry\":{\"id\":1}}", + new CustomComparator( + JSONCompareMode.STRICT, + new Customization("entry.id", + new RegularExpressionValueMatcher("\\d")) + )); + } + private void testPass(String expected, String actual, JSONCompareMode compareMode) - throws JSONException { String message = expected + " == " + actual + " (" + compareMode + ")"; JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); @@ -368,10 +648,209 @@ private void testPass(String expected, String actual, JSONCompareMode compareMod } private void testFail(String expected, String actual, JSONCompareMode compareMode) - throws JSONException { String message = expected + " != " + actual + " (" + compareMode + ")"; JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); Assert.assertTrue(message, result.failed()); } + + private void performAssertEqualsTestForMessageVerification( + Object expected, + Object actual, + Object strictMode) { + + String message = "Message"; + String testShouldFailMessage = "The test should fail so that the message in AssertionError could be verified."; + String strictModeMessage = "strictMode must be an instance of JSONCompareMode or Boolean"; + boolean assertEqualsFailed = true; + if(expected instanceof String && actual instanceof String && strictMode instanceof JSONComparator) { + try { + JSONAssert.assertEquals(message, (String) expected, (String) actual, (JSONComparator) strictMode); + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } + else if(expected instanceof String && actual instanceof JSONArray) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertEquals(message, (String) expected, (JSONArray) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertEquals(message, (String) expected, (JSONArray) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else if(expected instanceof JSONArray && actual instanceof JSONArray) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertEquals(message, (JSONArray) expected, (JSONArray) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertEquals(message, (JSONArray) expected, (JSONArray) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else if(expected instanceof String && actual instanceof String) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertEquals(message, (String) expected, (String) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertEquals(message, (String) expected, (String) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else if(expected instanceof String && actual instanceof JSONObject) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertEquals(message, (String) expected, (JSONObject) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertEquals(message, (String) expected, (JSONObject) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else if(expected instanceof JSONObject && actual instanceof JSONObject) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertEquals(message, (JSONObject) expected, (JSONObject) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertEquals(message, (JSONObject) expected, (JSONObject) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else { + fail("No overloaded method found to call"); + } + } + + private void performAssertNotEqualsTestForMessageVerification( + Object expected, + Object actual, + Object strictMode) + { + + String message = "Message"; + String testShouldFailMessage = "The test should fail so that the message in AssertionError could be verified."; + String strictModeMessage = "strictMode must be an instance of JSONCompareMode or Boolean"; + boolean assertEqualsFailed = true; + if(expected instanceof String && actual instanceof String && strictMode instanceof JSONComparator) { + try { + JSONAssert.assertNotEquals(message, (String) expected, (String) actual, (JSONComparator) strictMode); + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } + else if(expected instanceof String && actual instanceof JSONArray) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertNotEquals(message, (String) expected, (JSONArray) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertNotEquals(message, (String) expected, (JSONArray) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else if(expected instanceof JSONArray && actual instanceof JSONArray) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertNotEquals(message, (JSONArray) expected, (JSONArray) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertNotEquals(message, (JSONArray) expected, (JSONArray) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else if(expected instanceof String && actual instanceof String) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertNotEquals(message, (String) expected, (String) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertNotEquals(message, (String) expected, (String) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else if(expected instanceof String && actual instanceof JSONObject) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertNotEquals(message, (String) expected, (JSONObject) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertNotEquals(message, (String) expected, (JSONObject) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else if(expected instanceof JSONObject && actual instanceof JSONObject) { + try { + if(strictMode instanceof JSONCompareMode) { + JSONAssert.assertNotEquals(message, (JSONObject) expected, (JSONObject) actual, (JSONCompareMode) strictMode); + } else if(strictMode instanceof Boolean) { + JSONAssert.assertNotEquals(message, (JSONObject) expected, (JSONObject) actual, (Boolean) strictMode); + } else { + fail(strictModeMessage); + } + assertEqualsFailed = false; + fail(testShouldFailMessage); //will throw AssertionError + } catch (AssertionError ae) { + handleAssertionError(message, assertEqualsFailed, ae); + } + } else { + fail("No overloaded method found to call"); + } + } + + private void handleAssertionError(String message, boolean assertEqualsFailed, AssertionError ae) throws AssertionError { + if(assertEqualsFailed) { + verifyErrorMessage(message, ae); + } else { + throw ae; + } + } + + private void verifyErrorMessage(String message, AssertionError ae) { + assertTrue(ae.getMessage().contains(message)); + assertTrue(ae.getMessage().startsWith(message)); + } } diff --git a/src/test/java/org/skyscreamer/jsonassert/JSONCompareModeTest.java b/src/test/java/org/skyscreamer/jsonassert/JSONCompareModeTest.java index bf032293..df808855 100644 --- a/src/test/java/org/skyscreamer/jsonassert/JSONCompareModeTest.java +++ b/src/test/java/org/skyscreamer/jsonassert/JSONCompareModeTest.java @@ -1,3 +1,17 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; import static junit.framework.Assert.assertEquals; diff --git a/src/test/java/org/skyscreamer/jsonassert/JSONCompareTest.java b/src/test/java/org/skyscreamer/jsonassert/JSONCompareTest.java index c4727598..3bcb7377 100644 --- a/src/test/java/org/skyscreamer/jsonassert/JSONCompareTest.java +++ b/src/test/java/org/skyscreamer/jsonassert/JSONCompareTest.java @@ -1,15 +1,30 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.skyscreamer.jsonassert.JSONCompare.compareJSON; import static org.skyscreamer.jsonassert.JSONCompareMode.LENIENT; import static org.skyscreamer.jsonassert.JSONCompareMode.NON_EXTENSIBLE; +import static org.skyscreamer.jsonassert.JSONCompareMode.STRICT; import org.hamcrest.Description; import org.hamcrest.Matcher; -import org.json.JSONException; import org.junit.Test; import org.junit.internal.matchers.TypeSafeMatcher; @@ -18,124 +33,158 @@ */ public class JSONCompareTest { @Test - public void succeedsWithEmptyArrays() throws JSONException { + public void succeedsWithEmptyArrays() { assertTrue(compareJSON("[]", "[]", LENIENT).passed()); } @Test - public void reportsArraysOfUnequalLength() throws JSONException { + public void reportsArraysOfUnequalLength() { JSONCompareResult result = compareJSON("[4]", "[]", LENIENT); assertThat(result, failsWithMessage(equalTo("[]: Expected 1 values but got 0"))); } @Test - public void reportsArrayMissingExpectedElement() throws JSONException { + public void reportsArrayMissingExpectedElement() { JSONCompareResult result = compareJSON("[4]", "[7]", LENIENT); assertThat(result, failsWithMessage(equalTo("[]\nExpected: 4\n but none found\n ; []\nUnexpected: 7\n"))); + assertEquals(result.getFieldMissing().size(), 1); + assertEquals(result.getFieldUnexpected().size(), 1); } @Test - public void reportsMismatchedFieldValues() throws JSONException { + public void reportsMismatchedFieldValues() { JSONCompareResult result = compareJSON("{\"id\": 3}", "{\"id\": 5}", LENIENT); assertThat(result, failsWithMessage(equalTo("id\nExpected: 3\n got: 5\n"))); + assertThat(result, failsWithMessage(equalTo("id\nExpected: 3\n got: 5\n"))); } @Test - public void reportsMissingField() throws JSONException { + public void reportsMissingField() { JSONCompareResult result = compareJSON("{\"obj\": {\"id\": 3}}", "{\"obj\": {}}", LENIENT); assertThat(result, failsWithMessage(equalTo("obj\nExpected: id\n but none found\n"))); + assertEquals(result.getFieldMissing().size(), 1); } @Test - public void reportsUnexpectedArrayWhenExpectingObject() throws JSONException { + public void reportsUnexpectedArrayWhenExpectingObject() { JSONCompareResult result = compareJSON("{}", "[]", LENIENT); assertThat(result, failsWithMessage(equalTo("\nExpected: a JSON object\n got: a JSON array\n"))); } @Test - public void reportsUnexpectedObjectWhenExpectingArray() throws JSONException { + public void reportsUnexpectedObjectWhenExpectingArray() { JSONCompareResult result = compareJSON("[]", "{}", LENIENT); assertThat(result, failsWithMessage(equalTo("\nExpected: a JSON array\n got: a JSON object\n"))); } @Test - public void reportsUnexpectedNull() throws JSONException { + public void reportsUnexpectedNull() { JSONCompareResult result = compareJSON("{\"id\": 3}", "{\"id\": null}", LENIENT); assertThat(result, failsWithMessage(equalTo("id\nExpected: 3\n got: null\n"))); } @Test - public void reportsUnexpectedNonNull() throws JSONException { + public void reportsUnexpectedNonNull() { JSONCompareResult result = compareJSON("{\"id\": null}", "{\"id\": \"abc\"}", LENIENT); assertThat(result, failsWithMessage(equalTo("id\nExpected: null\n got: abc\n"))); } @Test - public void reportsUnexpectedFieldInNonExtensibleMode() throws JSONException { + public void reportsUnexpectedFieldInNonExtensibleMode() { JSONCompareResult result = compareJSON("{\"obj\": {}}", "{\"obj\": {\"id\": 3}}", NON_EXTENSIBLE); assertThat(result, failsWithMessage(equalTo("obj\nUnexpected: id\n"))); + assertEquals(result.getFieldUnexpected().size(), 1); } @Test - public void reportsMismatchedTypes() throws JSONException { + public void reportsMismatchedTypes() { JSONCompareResult result = compareJSON("{\"arr\":[]}", "{\"arr\":{}}", LENIENT); assertThat(result, failsWithMessage(equalTo("arr\nExpected: a JSON array\n got: a JSON object\n"))); } @Test - public void reportsWrongSimpleValueCountInUnorderedArray() throws JSONException { + public void reportsWrongSimpleValueCountInUnorderedArray() { JSONCompareResult result = compareJSON("[5, 5]", "[5, 7]", LENIENT); assertThat(result, failsWithMessage(equalTo("[]: Expected 2 occurrence(s) of 5 but got 1 occurrence(s) ; []\nUnexpected: 7\n"))); + assertEquals(result.getFieldUnexpected().size(), 1); } @Test - public void reportsMissingJSONObjectWithUniqueKeyInUnorderedArray() throws JSONException { + public void reportsMissingJSONObjectWithUniqueKeyInUnorderedArray() { JSONCompareResult result = compareJSON("[{\"id\" : 3}]", "[{\"id\" : 5}]", LENIENT); assertThat(result, failsWithMessage(equalTo("[id=3]\nExpected: a JSON object\n but none found\n ; " + "[id=5]\nUnexpected: a JSON object\n"))); + assertEquals(result.getFieldMissing().size(), 1); + assertEquals(result.getFieldUnexpected().size(), 1); } @Test - public void reportsUnmatchedJSONObjectInUnorderedArray() throws JSONException { + public void reportsUnmatchedJSONObjectInUnorderedArray() { JSONCompareResult result = compareJSON("[{\"address\" : {\"street\" : \"Acacia Avenue\"}}]", "[{\"age\" : 23}]", LENIENT); assertThat(result, failsWithMessage(equalTo("[0] Could not find match for element {\"address\":{\"street\":\"Acacia Avenue\"}}"))); } @Test - public void succeedsWithNestedJSONObjectsInUnorderedArray() throws JSONException { + public void succeedsWithNestedJSONObjectsInUnorderedArray() { assertTrue(compareJSON("[{\"address\" : {\"street\" : \"Acacia Avenue\"}}, 5]", "[5, {\"address\" : {\"street\" : \"Acacia Avenue\"}}]", LENIENT).passed()); } @Test - public void succeedsWithJSONObjectsWithNonUniqueKeyInUnorderedArray() throws JSONException { + public void succeedsWithJSONObjectsWithNonUniqueKeyInUnorderedArray() { String jsonDocument = "[{\"age\" : 43}, {\"age\" : 43}]"; assertTrue(compareJSON(jsonDocument, jsonDocument, LENIENT).passed()); } @Test - public void succeedsWithSomeNestedJSONObjectsInUnorderedArray() throws JSONException { + public void succeedsWithSomeNestedJSONObjectsInUnorderedArray() { String jsonDocument = "[{\"age\" : 43}, {\"age\" : {\"years\" : 43}}]"; assertTrue(compareJSON(jsonDocument, jsonDocument, LENIENT).passed()); } @Test - public void reportsUnmatchesIntegerValueInUnorderedArrayContainingJSONObject() throws JSONException { + public void reportsUnmatchesIntegerValueInUnorderedArrayContainingJSONObject() { JSONCompareResult result = compareJSON("[{\"address\" : {\"street\" : \"Acacia Avenue\"}}, 5]", "[{\"address\" : {\"street\" : \"Acacia Avenue\"}}, 2]", LENIENT); assertThat(result, failsWithMessage(equalTo("[1] Could not find match for element 5"))); } @Test - public void reportsUnmatchedJSONArrayWhereOnlyExpectedContainsJSONObjectWithUniqueKey() throws JSONException { + public void reportsUnmatchedJSONArrayWhereOnlyExpectedContainsJSONObjectWithUniqueKey() { JSONCompareResult result = compareJSON("[{\"id\": 3}]", "[{}]", LENIENT); assertThat(result, failsWithMessage(equalTo("[0] Could not find match for element {\"id\":3}"))); } @Test - public void reportsUnmatchedJSONArrayWhereExpectedContainsJSONObjectWithUniqueKeyButActualContainsElementOfOtherType() throws JSONException { + public void reportsUnmatchedJSONArrayWhereExpectedContainsJSONObjectWithUniqueKeyButActualContainsElementOfOtherType() { JSONCompareResult result = compareJSON("[{\"id\": 3}]", "[5]", LENIENT); assertThat(result, failsWithMessage(equalTo("[0] Could not find match for element {\"id\":3}"))); } + @Test + public void reportsUnmatchedJSONArrayWhereExpectedContainsNonnullIntegerButActualContainsNullElement() { + JSONCompareResult result = compareJSON("[ 3 ]", "[ null ]", LENIENT); + assertThat(result, failsWithMessage(equalTo("[]\nExpected: 3\n but none found\n ; " + + "[]\nUnexpected: null\n"))); + } + + @Test + public void reportsUnmatchedJSONArrayWhereExpectedContainsNullElementButActualContainsNonnullInteger() { + JSONCompareResult result = compareJSON("[ null ]", "[ 3 ]", LENIENT); + assertThat(result, failsWithMessage(equalTo("[]\nExpected: null\n but none found\n ; " + + "[]\nUnexpected: 3\n"))); + } + + @Test + public void reportsStrictUnmatchedJSONArrayWhereExpectedContainsNonnullIntegerButActualContainsNullElement() { + JSONCompareResult result = compareJSON("[ 3 ]", "[ null ]", STRICT); + assertThat(result, failsWithMessage(equalTo("[0]\nExpected: 3\n got: null\n"))); + } + + @Test + public void reportsStrictUnmatchedJSONArrayWhereExpectedContainsNullButActualContainsNonnullInteger() { + JSONCompareResult result = compareJSON("[ null ]", "[ 3 ]", STRICT); + assertThat(result, failsWithMessage(equalTo("[0]\nExpected: null\n got: 3\n"))); + } + private Matcher failsWithMessage(final Matcher expectedMessage) { return new TypeSafeMatcher() { @Override diff --git a/src/test/java/org/skyscreamer/jsonassert/JSONCustomComparatorTest.java b/src/test/java/org/skyscreamer/jsonassert/JSONCustomComparatorTest.java index 897f1ad5..53546166 100644 --- a/src/test/java/org/skyscreamer/jsonassert/JSONCustomComparatorTest.java +++ b/src/test/java/org/skyscreamer/jsonassert/JSONCustomComparatorTest.java @@ -1,6 +1,19 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert; -import org.json.JSONException; import org.junit.Test; import org.skyscreamer.jsonassert.comparator.CustomComparator; import org.skyscreamer.jsonassert.comparator.JSONComparator; @@ -35,6 +48,75 @@ public class JSONCustomComparatorTest { " }\n" + "}"; + String simpleWildcardActual = "{\n" + + " \"foo\": {\n" + + " \"bar1\": {\n" + + " \"baz\": \"actual\"\n" + + " },\n" + + " \"bar2\": {\n" + + " \"baz\": \"actual\"\n" + + " }\n" + + " }\n" + + "}"; + String simpleWildcardExpected = "{\n" + + " \"foo\": {\n" + + " \"bar1\": {\n" + + " \"baz\": \"expected\"\n" + + " },\n" + + " \"bar2\": {\n" + + " \"baz\": \"expected\"\n" + + " }\n" + + " }\n" + + "}"; + + String deepWildcardActual = "{\n" + + " \"root\": {\n" + + " \"baz\": \"actual\",\n" + + " \"foo\": {\n" + + " \"baz\": \"actual\",\n" + + " \"bar\": {\n" + + " \"baz\": \"actual\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; + String deepWildcardExpected = "{\n" + + " \"root\": {\n" + + " \"baz\": \"expected\",\n" + + " \"foo\": {\n" + + " \"baz\": \"expected\",\n" + + " \"bar\": {\n" + + " \"baz\": \"expected\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; + + String rootDeepWildcardActual = "{\n" + + " \"baz\": \"actual\",\n" + + " \"root\": {\n" + + " \"baz\": \"actual\",\n" + + " \"foo\": {\n" + + " \"baz\": \"actual\",\n" + + " \"bar\": {\n" + + " \"baz\": \"actual\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; + String rootDeepWildcardExpected = "{\n" + + " \"baz\": \"expected\",\n" + + " \"root\": {\n" + + " \"baz\": \"expected\",\n" + + " \"foo\": {\n" + + " \"baz\": \"expected\",\n" + + " \"bar\": {\n" + + " \"baz\": \"expected\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; + int comparatorCallCount = 0; ValueMatcher comparator = new ValueMatcher() { @Override @@ -45,7 +127,7 @@ public boolean equal(Object o1, Object o2) { }; @Test - public void whenPathMatchesInCustomizationThenCallCustomMatcher() throws JSONException { + public void whenPathMatchesInCustomizationThenCallCustomMatcher() { JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("first", comparator)); JSONCompareResult result = compareJSON(expected, actual, jsonCmp); assertTrue(result.getMessage(), result.passed()); @@ -53,11 +135,34 @@ public void whenPathMatchesInCustomizationThenCallCustomMatcher() throws JSONExc } @Test - public void whenDeepPathMatchesCallCustomMatcher() throws JSONException { + public void whenDeepPathMatchesCallCustomMatcher() { JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("outer.inner.value", comparator)); JSONCompareResult result = compareJSON(deepExpected, deepActual, jsonCmp); assertTrue(result.getMessage(), result.passed()); assertEquals(1, comparatorCallCount); } + @Test + public void whenSimpleWildcardPathMatchesCallCustomMatcher() { + JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("foo.*.baz", comparator)); + JSONCompareResult result = compareJSON(simpleWildcardExpected, simpleWildcardActual, jsonCmp); + assertTrue(result.getMessage(), result.passed()); + assertEquals(2, comparatorCallCount); + } + + @Test + public void whenDeepWildcardPathMatchesCallCustomMatcher() { + JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("root.**.baz", comparator)); + JSONCompareResult result = compareJSON(deepWildcardExpected, deepWildcardActual, jsonCmp); + assertTrue(result.getMessage(), result.passed()); + assertEquals(3, comparatorCallCount); + } + + @Test + public void whenRootDeepWildcardPathMatchesCallCustomMatcher() { + JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("**.baz", comparator)); + JSONCompareResult result = compareJSON(rootDeepWildcardExpected, rootDeepWildcardActual, jsonCmp); + assertTrue(result.getMessage(), result.passed()); + assertEquals(4, comparatorCallCount); + } } diff --git a/src/test/java/org/skyscreamer/jsonassert/RegularExpressionValueMatcherTest.java b/src/test/java/org/skyscreamer/jsonassert/RegularExpressionValueMatcherTest.java new file mode 100644 index 00000000..18539bc0 --- /dev/null +++ b/src/test/java/org/skyscreamer/jsonassert/RegularExpressionValueMatcherTest.java @@ -0,0 +1,117 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.skyscreamer.jsonassert; + +import org.junit.Assert; + +import org.junit.Test; +import org.skyscreamer.jsonassert.comparator.CustomComparator; + +/** + * Unit tests for RegularExpressionValueMatcher + * + * @author Duncan Mackinder + * + */ +public class RegularExpressionValueMatcherTest { + private static final String ARRAY_ELEMENT_PREFIX = "d.results[0].__metadata.uri"; + private static final String JSON_STRING_WITH_ARRAY = "{d:{results:[{__metadata:{uri:\"http://localhost:80/Person('1')\",type:Person},id:1}]}}"; + private static final String CONSTANT_URI_REGEX_EXPECTED_JSON = "{d:{results:[{__metadata:{uri:X}}]}}"; + + private void doTest(String jsonPath, String regex, String expectedJSON, + String actualJSON) { + JSONAssert.assertEquals(expectedJSON, actualJSON, new CustomComparator( + JSONCompareMode.STRICT_ORDER, new Customization(jsonPath, + new RegularExpressionValueMatcher(regex)))); + } + + @Test + public void constantRegexWithSimplePathMatchsStringAttribute() { + doTest("a", "v.", "{a:x}", "{a:v1}"); + } + + @Test + public void constantRegexWithThreeLevelPathMatchsStringAttribute() { + doTest("a.b.c", ".*Is.*", "{a:{b:{c:x}}}", "{a:{b:{c:thisIsAString}}}"); + } + + @Test + public void dynamicRegexWithSimplePathMatchsStringAttribute() { + doTest("a", null, "{a:\"v.\"}", "{a:v1}"); + } + + @Test + public void dynamicRegexWithThreeLevelPathMatchsStringAttribute() { + doTest("a.b.c", null, "{a:{b:{c:\".*Is.*\"}}}", + "{a:{b:{c:thisIsAString}}}"); + } + + @Test + public void constantRegexMatchesStringAttributeInsideArray() { + doTest(ARRAY_ELEMENT_PREFIX, "http://localhost:80/Person\\('\\d+'\\)", CONSTANT_URI_REGEX_EXPECTED_JSON, JSON_STRING_WITH_ARRAY); + } + + @Test + public void dynamicRegexMatchesStringAttributeInsideArray() { + doTest(ARRAY_ELEMENT_PREFIX, null, "{d:{results:[{__metadata:{uri:\"http://localhost:80/Person\\\\('\\\\d+'\\\\)\"}}]}}", JSON_STRING_WITH_ARRAY); + } + + @Test + public void dynamicRegexMatchesStringAttributeInsideArrayWithNoArgConstructor() { + JSONAssert.assertEquals("{d:{results:[{__metadata:{uri:\"http://localhost:80/Person\\\\('\\\\d+'\\\\)\"}}]}}", JSON_STRING_WITH_ARRAY, new CustomComparator( + JSONCompareMode.STRICT_ORDER, new Customization(ARRAY_ELEMENT_PREFIX, + new RegularExpressionValueMatcher()))); + } + + @Test + public void failsWhenDynamicRegexInvalid() { + try { + doTest(ARRAY_ELEMENT_PREFIX, null, "{d:{results:[{__metadata:{uri:\"http://localhost:80/Person('\\\\d+'\\\\)\"}}]}}", JSON_STRING_WITH_ARRAY); + } + catch (AssertionError e) { + Assert.assertTrue("Invalid exception message returned: "+ e.getMessage(), e.getMessage().startsWith(ARRAY_ELEMENT_PREFIX + ": Dynamic expected pattern invalid: ")); + } + } + + @Test + public void failsWhenDynamicRegexDoesNotMatchStringAttributeInsideArray() { + try { + doTest(ARRAY_ELEMENT_PREFIX, null, "{d:{results:[{__metadata:{uri:\"http://localhost:80/Person\\\\('\\\\w+'\\\\)\"}}]}}", JSON_STRING_WITH_ARRAY); + } + catch (AssertionError e) { + Assert.assertTrue("Invalid exception message returned: "+ e.getMessage(), e.getMessage().startsWith(ARRAY_ELEMENT_PREFIX + ": Dynamic expected pattern did not match value")); + } + } + + @Test + public void failsWhenConstantRegexInvalid() { + try { + doTest(ARRAY_ELEMENT_PREFIX, "http://localhost:80/Person\\\\['\\\\d+'\\\\)", CONSTANT_URI_REGEX_EXPECTED_JSON, JSON_STRING_WITH_ARRAY); + } + catch (IllegalArgumentException e) { + Assert.assertTrue("Invalid exception message returned: "+ e.getMessage(), e.getMessage().startsWith("Constant expected pattern invalid: ")); + } + } + + @Test + public void failsWhenConstantRegexDoesNotMatchStringAttributeInsideArray() { + try { + doTest(ARRAY_ELEMENT_PREFIX, "http://localhost:80/Person\\\\('\\\\w+'\\\\)", CONSTANT_URI_REGEX_EXPECTED_JSON, JSON_STRING_WITH_ARRAY); + } + catch (AssertionError e) { + Assert.assertTrue("Invalid exception message returned: "+ e.getMessage(), e.getMessage().startsWith(ARRAY_ELEMENT_PREFIX + ": Constant expected pattern did not match value")); + } + } +} diff --git a/src/test/java/org/skyscreamer/jsonassert/comparator/ArraySizeComparatorTest.java b/src/test/java/org/skyscreamer/jsonassert/comparator/ArraySizeComparatorTest.java new file mode 100644 index 00000000..7af97ee5 --- /dev/null +++ b/src/test/java/org/skyscreamer/jsonassert/comparator/ArraySizeComparatorTest.java @@ -0,0 +1,141 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package org.skyscreamer.jsonassert.comparator; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.text.MessageFormat; + +import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; + +/** + * Unit tests for ArraySizeComparator + * + * @author Duncan Mackinder + * + */ +public class ArraySizeComparatorTest { + private static final String twoElementArray = "{a:[b,c]}"; + + private void doTest(String expectedJSON, String actualJSON) + { + JSONAssert.assertEquals(expectedJSON, actualJSON, new ArraySizeComparator(JSONCompareMode.STRICT_ORDER)); + } + + private void doFailingMatchTest(String expectedJSON, String actualJSON, String expectedMessagePattern) { + try { + doTest(expectedJSON, actualJSON); + } + catch (AssertionError e) { + String failureMessage = MessageFormat.format("Exception message ''{0}'', does not match expected pattern ''{1}''", e.getMessage(), expectedMessagePattern); + assertTrue(failureMessage, e.getMessage().matches(expectedMessagePattern)); + return; + } + fail("AssertionError not thrown"); + } + + @Test + public void succeedsWhenExactSizeExpected() { + doTest("{a:[2]}", twoElementArray); + } + + @Test + public void succeedsWhenSizeWithinExpectedRange() { + doTest("{a:[1,3]}", twoElementArray); + } + + @Test + public void succeedsWhenSizeIsMinimumOfExpectedRange() { + doTest("{a:[2,4]}", twoElementArray); + } + + @Test + public void succeedsWhenSizeIsMaximumOfExpectedRange() { + doTest("{a:[1,2]}", twoElementArray); + } + + @Test + public void failsWhenExpectedArrayTooShort() { + doFailingMatchTest("{a:[]}", twoElementArray, "a\\[\\]: invalid expectation: expected array should contain either 1 or 2 elements but contains 0 elements"); + } + + @Test + public void failsWhenExpectedArrayTooLong() { + doFailingMatchTest("{a:[1,2,3]}", twoElementArray, "a\\[\\]: invalid expectation: expected array should contain either 1 or 2 elements but contains 3 elements"); + } + + @Test + public void failsWhenExpectedNotAllSimpleTypes() { + doFailingMatchTest("{a:[{y:1},2]}", twoElementArray, "a\\[\\]: invalid expectation: minimum expected array size '\\{\"y\":1\\}' not a number"); + } + + @Test + public void failsWhenExpectedMinimumTooSmall() { + doFailingMatchTest("{a:[-1,6]}", twoElementArray, "a\\[\\]: invalid expectation: minimum expected array size '-1' negative"); + } + + @Test + public void failsWhenExpectedMaximumTooSmall() { + doFailingMatchTest("{a:[8,6]}", twoElementArray, "a\\[\\]: invalid expectation: maximum expected array size '6' less than minimum expected array size '8'"); + } + + @Test + public void failsWhenExpectedArraySizeNotANumber() { + doFailingMatchTest("{a:[X]}", twoElementArray, "a\\[\\]: invalid expectation: expected array size 'X' not a number"); + } + + @Test + public void failsWhenFirstExpectedArrayElementNotANumber() { + doFailingMatchTest("{a:[MIN,6]}", twoElementArray, "a\\[\\]: invalid expectation: minimum expected array size 'MIN' not a number"); + } + + @Test + public void failsWhenSecondExpectedArrayElementNotANumber() { + doFailingMatchTest("{a:[8,MAX]}", twoElementArray, "a\\[\\]: invalid expectation: maximum expected array size 'MAX' not a number"); + } + + @Test + public void failsWhenActualArrayTooShort() { + doFailingMatchTest("{a:[3]}", twoElementArray, "a\\[\\]\\s*Expected:\\s*array size of 3 elements\\s*got:\\s*2 elements\\s*"); + } + + @Test + public void failsWhenActualArrayLongerThanExpectedLength() { + doFailingMatchTest("{a:[1]}", twoElementArray, "a\\[\\]\\s*Expected:\\s*array size of 1 elements\\s*got:\\s*2 elements\\s*"); + } + + @Test + public void failsWhenActualArrayLongerThanMaxOfExpectedRange() { + doFailingMatchTest("{a:[0,1]}", twoElementArray, "a\\[\\]\\s*Expected:\\s*array size of 0 to 1 elements\\s*got:\\s*2 elements\\s*"); + } + + /* + * Following tests are copied from ArraySizeComparator JavaDoc and are include to ensure code as documented work as expected. + */ + + @Test + public void succeedsWhenActualArrayContainsExactly3Elements() { + JSONAssert.assertEquals("{a:[3]}", "{a:[7, 8, 9]}", new ArraySizeComparator(JSONCompareMode.LENIENT)); + } + + @Test + public void succeedsWhenActualArrayContainsBetween2And6Elements() { + JSONAssert.assertEquals("{a:[2,6]}", "{a:[7, 8, 9]}", new ArraySizeComparator(JSONCompareMode.LENIENT)); + } + +} diff --git a/src/test/java/org/skyscreamer/jsonassert/comparator/CustomComparatorTest.java b/src/test/java/org/skyscreamer/jsonassert/comparator/CustomComparatorTest.java index 4f5ff05c..b48ffb59 100644 --- a/src/test/java/org/skyscreamer/jsonassert/comparator/CustomComparatorTest.java +++ b/src/test/java/org/skyscreamer/jsonassert/comparator/CustomComparatorTest.java @@ -1,8 +1,21 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert.comparator; import junit.framework.Assert; import org.json.JSONArray; -import org.json.JSONException; import org.junit.Test; import org.skyscreamer.jsonassert.JSONCompare; import org.skyscreamer.jsonassert.JSONCompareMode; @@ -20,7 +33,7 @@ public ArrayOfJsonObjectsComparator(JSONCompareMode mode) { } @Override - public void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException { + public void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) { compareJSONArrayOfJsonObjects(prefix, expected, actual, result); } } diff --git a/src/test/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtilTest.java b/src/test/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtilTest.java index e07a8838..65f03b1e 100644 --- a/src/test/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtilTest.java +++ b/src/test/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtilTest.java @@ -1,3 +1,17 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + package org.skyscreamer.jsonassert.comparator; import junit.framework.Assert;
    + * Behavior of JSONCompareMode + *
     ExtensibleStrict Ordering
    STRICTnoyes
    LENIENTyesno