├── .github └── workflows │ └── deploy-website.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main └── java │ └── org │ └── skyscreamer │ └── jsonassert │ ├── ArrayValueMatcher.java │ ├── Customization.java │ ├── FieldComparisonFailure.java │ ├── JSONAssert.java │ ├── JSONCompare.java │ ├── JSONCompareMode.java │ ├── JSONCompareResult.java │ ├── JSONParser.java │ ├── LocationAwareValueMatcher.java │ ├── RegularExpressionValueMatcher.java │ ├── ValueMatcher.java │ ├── ValueMatcherException.java │ └── comparator │ ├── AbstractComparator.java │ ├── ArraySizeComparator.java │ ├── CustomComparator.java │ ├── DefaultComparator.java │ ├── JSONComparator.java │ └── JSONCompareUtil.java ├── site └── resources │ ├── CNAME │ ├── cookbook.html │ ├── css │ └── style.css │ ├── index.html │ └── quickstart.html └── test └── java └── org └── skyscreamer └── jsonassert ├── ArrayValueMatcherTest.java ├── DependencyTest.java ├── JSONArrayWithNullTest.java ├── JSONAssertTest.java ├── JSONCompareModeTest.java ├── JSONCompareTest.java ├── JSONCustomComparatorTest.java ├── RegularExpressionValueMatcherTest.java └── comparator ├── ArraySizeComparatorTest.java ├── CustomComparatorTest.java └── JSONCompareUtilTest.java /.github/workflows/deploy-website.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch - disabled, manual only 6 | #push: 7 | # branches: ["master"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | - name: Setup Pages 35 | uses: actions/configure-pages@v5 36 | - uses: actions/setup-java@v4 37 | with: 38 | java-version: '17' 39 | distribution: 'temurin' 40 | cache: maven 41 | - name: Run the Maven javadoc command 42 | run: mvn javadoc:aggregate -DreportOutputDirectory=src/site/resources/apidocs 43 | - name: Upload artifact 44 | uses: actions/upload-pages-artifact@v3 45 | with: 46 | # Upload entire repository 47 | path: './src/site/resources' 48 | - name: Deploy to GitHub Pages 49 | id: deployment 50 | uses: actions/deploy-pages@v4 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | target -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | Version 2.0-rc1 - 7/28/2024 5 | ------------------- 6 | - Release candidate 7 | - ** Switches JSON implementation to use org.json:json:20240303 ** 8 | - Deployment still built with Java version 8 to maximize compatibility 9 | - Cannot insert null directly into JSONArray without casting. Recommend to use JSONObject.Null 10 | - JSONException is now a RuntimeException. Is not defined as thrown in method signatures anynmore. 11 | 12 | Version 1.5.3 - 6/28/2024 13 | ------------------------- 14 | - Revert Java release version from 21 to 8 due to breaking older compilers. 15 | 16 | Version 1.5.2 - 6/14/2024 17 | ------------------------- 18 | - Fix CVE-2020-15250 JUnit vulnerability (https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15250). Bump 19 | dependencies. 20 | - Add gitIgnore file 21 | - README syntax error fix 22 | - Accidentally upgraded release to Java version 21 23 | 24 | Version 1.5.1 - 7/4/2022 25 | ------------------------ 26 | Going to try to catch up on some ancient PRs, mainly around security and cleanup. Starting with accepted PRs that 27 | didn't get released yet. To be followed hopefully shortly with another release. 28 | - Added convenience methods for JSONObject comparison using a custom JSONComparator (thanks jakob-o@!) 29 | - Fix issue #105: Issue when comparing JSONArray if any value is null (thanks suraj1291993@!) 30 | - Fixes security vulnerability associated with older version of junit 31 | 32 | Version 1.5.0 - 3/19/2017 33 | ------------------------- 34 | - JSONassert now supports user-supplied error messages (thanks yasin3061@!) 35 | - Some refactoring / code health cleanup (thanks picimako@!) 36 | - License headers on individual files 37 | - Java 8 friendly javadocs 38 | 39 | Version 1.4.0 - 10/30/2016 40 | -------------------------- 41 | - Change the implementation for org.json to one with a more open license 42 | - Fix null pointer exception (issue #48) 43 | - Support wildcards in Customization.path 44 | 45 | Version 1.3.0 - 12/16/2015 46 | -------------------------- 47 | - Fix & improve ArrayValueMatcher JavaDoc (thanks dmackinder@!) 48 | Fix final JavaDoc example and add new example showing how to verify 49 | every array element using a custom comparator 50 | - Fix URL in pom.xml (aukevanleeuwen@) 51 | - Update JSONCompareResult.java adding 2 new lists for missing and unexpected fileds (thanks riccorazza@!) 52 | - Includes missing imports in test class (thanks javierseixas@!) 53 | 54 | Version 1.2.3 - 2/5/2014 55 | ------------------------ 56 | - This edition brought to you by dmackinder (thanks dmackinder!) 57 | - Added array size comparator enhancements. 58 | - Added ArrayValueMatcher to simplify verification of range of array elements. 59 | - Improve diagnostics from RegularExpressionValueMatcher. 60 | - Deprecated former Customization.matches() signature 61 | 62 | Version 1.2.2 - 12/31/2013 63 | -------------------------- 64 | - Add support for JSONString 65 | 66 | Version 1.2.1 - 10/24/2013 67 | -------------------------- 68 | - Remove commons-collection dependency 69 | - Updated Customization class to allow path-matching, and matching of expected and actual values with user-provided 70 | EqualityComparator. 71 | - Added AssertNotEquals 72 | 73 | Version 1.2.0 - 3/17/2013 74 | ------------------------- 75 | - Fixed handling comparison of equivalent values across long, int, and double 76 | - Add JSONCompareMode to asserts to allow for more options than strict/not-strict 77 | - Added hooks to override/extend comparison behavior via JSONComparator 78 | 79 | Version 1.1.1 - 10/15/2012 80 | -------------------------- 81 | - Return diagnostics (instead of throwing an exception) when comparing JSONObject and JSONArray 82 | - Expose field comparison results as a list in JSONCompareResult 83 | - Fix bug where actual JSON array doesn't contain JSONObjects with unique keys 84 | - Improve diagnostics 85 | - Unify some diagnostics 86 | - Fix handling of arrays of booleans 87 | 88 | Version 1.1.0 - 9/3/2012 89 | ------------------------ 90 | - Added withStrictOrdering() and withExtensible() to JSONCompareMode 91 | - Javadoc fixes 92 | - Fix bug where expected and actual were reversed 93 | - Fix bug where nulls gave false positives in some cases 94 | - Simplified publishing Javadocs 95 | 96 | Version 1.0.0 - 4/5/2012 97 | ------------------------ 98 | Initial release! 99 | 100 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JSONassert 2 | ========== 3 | 4 | Write JSON unit tests in less code. Great for testing REST interfaces. 5 | 6 | Summary 7 | ------- 8 | 9 | Write JSON tests as if you are comparing a string. Under the covers, JSONassert converts your string into a JSON object and compares the logical structure and data with the actual JSON. When _strict_ is set to false (recommended), it forgives reordering data and extending results (as long as all the expected elements are there), making tests less brittle. 10 | 11 | Supported test frameworks: 12 | 13 | * [JUnit](http://junit.org) 14 | 15 | Examples 16 | -------- 17 | 18 | In JSONassert you write and maintain something like this: 19 | 20 | JSONObject data = getRESTData("/friends/367.json"); 21 | String expected = "{friends:[{id:123,name:\"Corby Page\"},{id:456,name:\"Carter Page\"}]}"; 22 | JSONAssert.assertEquals(expected, data, false); 23 | 24 | instead of all this: 25 | 26 |

 27 | JSONObject data = getRESTData("/friends/367.json");
 28 | Assert.assertTrue(data.has("friends"));
 29 | Object friendsObject = data.get("friends");
 30 | Assert.assertTrue(friendsObject instanceof JSONArray);
 31 | JSONArray friends = (JSONArray) friendsObject;
 32 | Assert.assertEquals(2, friends.length());
 33 | JSONObject friend1Obj = friends.getJSONObject(0);
 34 | Assert.assertTrue(friend1Obj.has("id"));
 35 | Assert.assertTrue(friend1Obj.has("name"));
 36 | JSONObject friend2Obj = friends.getJSONObject(1);
 37 | Assert.assertTrue(friend2Obj.has("id"));
 38 | Assert.assertTrue(friend2Obj.has("name"));
 39 | 
 40 | if ("Carter Page".equals(friend1Obj.getString("name"))) {
 41 |     Assert.assertEquals(456, friend1Obj.getInt("id"));
 42 |     Assert.assertEquals("Corby Page", friend2Obj.getString("name"));
 43 |     Assert.assertEquals(123, friend2Obj.getInt("id"));
 44 | }
 45 | else if ("Corby Page".equals(friend1Obj.getString("name"))) {
 46 |     Assert.assertEquals(123, friend1Obj.getInt("id"));
 47 |     Assert.assertEquals("Carter Page", friend2Obj.getString("name"));
 48 |     Assert.assertEquals(456, friend2Obj.getInt("id"));
 49 | }
 50 | else {
 51 |     Assert.fail("Expected either Carter or Corby, Got: " + friend1Obj.getString("name"));
 52 | }
 53 | 
54 | 55 | Error Messages 56 | -------------- 57 | 58 | We tried to make error messages easy to understand. This is really important, since it gets hard for the eye to pick out the difference, particularly in long JSON strings. For example: 59 | 60 | String expected = "{id:1,name:\"Joe\",friends:[{id:2,name:\"Pat\",pets:[\"dog\"]},{id:3,name:\"Sue\",pets:[\"bird\",\"fish\"]}],pets:[]}"; 61 | String actual = "{id:1,name:\"Joe\",friends:[{id:2,name:\"Pat\",pets:[\"dog\"]},{id:3,name:\"Sue\",pets:[\"cat\",\"fish\"]}],pets:[]}" 62 | JSONAssert.assertEquals(expected, actual, false); 63 | 64 | returns the following: 65 | 66 | friends[id=3].pets[]: Expected bird, but not found ; friends[id=3].pets[]: Contains cat, but not expected 67 | 68 | Which tells you that the pets array under the friend where id=3 was supposed to contain "bird", but had "cat" instead. (Maybe the cat ate the bird?) 69 | 70 | * * * 71 | 72 | QuickStart 73 | ---------- 74 | 75 | To use, [download the JAR](https://github.com/skyscreamer/JSONassert/releases) or add the following to your project's pom.xml: 76 | 77 | 78 | org.skyscreamer 79 | jsonassert 80 | 2.0-rc1 81 | test 82 | 83 | 84 | Write tests like this: 85 | 86 | JSONAssert.assertEquals(expectedJSONString, actualJSON, strictMode); 87 | 88 | [More examples in our cookbook](http://jsonassert.skyscreamer.org/cookbook.html) 89 | 90 | * * * 91 | 92 | Who uses JSONassert? 93 | -------------------- 94 | + [yoga](https://github.com/skyscreamer/yoga) - A relational REST framework 95 | + [hamcrest-json](https://github.com/hertzsprung/hamcrest-json) - Hamcrest matchers for comparing JSON documents 96 | + [Mule ESB](http://www.mulesoft.org/) 97 | + [GroupDocs](http://groupdocs.com/) 98 | + [Shazam](http://www.shazam.com/) 99 | + [Thucydides](http://thucydides.net/) 100 | + [and over a thousand more](https://mvnrepository.com/artifact/org.skyscreamer/jsonassert)... 101 | 102 | * * * 103 | 104 | org.json 105 | -------- 106 | 107 | As of v2, JSONAssert uses @stleary's [JSON-java](https://github.com/stleary/JSON-java) implementation of org.json, the 108 | most commonly used reference implementation for JSON in Java. 109 | 110 | Resources 111 | --------- 112 | 113 | [JavaDoc](http://jsonassert.skyscreamer.org/apidocs/index.html) 114 | 115 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.skyscreamer 6 | jsonassert 7 | 2.0-rc1 8 | jar 9 | 10 | JSONassert 11 | Write JSON unit tests in less code. Great for testing REST interfaces. 12 | https://github.com/skyscreamer/JSONassert 13 | 14 | 15 | 8 16 | 17 | 18 | 19 | 20 | The Apache Software License, Version 2.0 21 | http://www.apache.org/licenses/LICENSE-2.0.txt 22 | repo 23 | 24 | 25 | 26 | scm:git:git@github.com:skyscreamer/JSONassert.git 27 | scm:git:git@github.com:skyscreamer/JSONassert.git 28 | git@github.com:skyscreamer/JSONassert.git 29 | 30 | 31 | 32 | carterpage 33 | Carter Page 34 | carter@skyscreamer.org 35 | 36 | 37 | hertzsprung 38 | James Shaw 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.json 46 | json 47 | 20240303 48 | 49 | 50 | junit 51 | junit 52 | 4.13.2 53 | test 54 | 55 | 56 | org.hamcrest 57 | hamcrest 58 | 2.2 59 | test 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-compiler-plugin 68 | 3.11.0 69 | 70 | 71 | org.sonatype.plugins 72 | nexus-staging-maven-plugin 73 | 1.6.7 74 | true 75 | 76 | ossrh 77 | https://oss.sonatype.org/ 78 | false 79 | 80 | 81 | 82 | com.thoughtworks.xstream 83 | xstream 84 | 1.4.15 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | github-project-site 94 | gitsite:git@github.com/skyscreamer/JSONassert.git 95 | 96 | 97 | ossrh 98 | https://oss.sonatype.org/content/repositories/snapshots 99 | 100 | 101 | 102 | 103 | 104 | deploy 105 | 106 | 107 | 108 | org.apache.maven.plugins 109 | maven-source-plugin 110 | 2.2.1 111 | 112 | 113 | attach-sources 114 | 115 | jar-no-fork 116 | 117 | 118 | 119 | 120 | 121 | org.apache.maven.plugins 122 | maven-javadoc-plugin 123 | 2.9.1 124 | 125 | 126 | attach-javadocs 127 | 128 | jar 129 | 130 | 131 | 132 | 133 | ${java.home}/bin/javadoc 134 | ${maven.compiler.release} 135 | 136 | 137 | 138 | org.apache.maven.plugins 139 | maven-gpg-plugin 140 | 1.5 141 | 142 | 143 | sign-artifacts 144 | verify 145 | 146 | sign 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/ArrayValueMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import java.text.MessageFormat; 18 | 19 | import org.json.JSONArray; 20 | import org.json.JSONException; 21 | import org.skyscreamer.jsonassert.comparator.JSONComparator; 22 | 23 | /** 24 | *

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

34 | * 35 | *

Some examples of typical usage idioms listed below.

36 | * 37 | *

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

38 | * 39 | *
{@code
 40 |  * {a:[{background:white, id:1, type:row},
 41 |  *     {background:grey,  id:2, type:row},
 42 |  *     {background:white, id:3, type:row},
 43 |  *     {background:grey,  id:4, type:row}]}
 44 |  * }
45 | * 46 | *

then:

47 | * 48 | *

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

49 | * 50 | *
{@code
 51 |  * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
 52 |  * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0));
 53 |  * JSONAssert.assertEquals("{a:[{id:1}]}", ARRAY_OF_JSONOBJECTS,
 54 |  *     new CustomComparator(JSONCompareMode.LENIENT, customization));
 55 |  * }
 56 |  *
 57 |  * 

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

58 | * 59 | *

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

60 | * 61 | *
{@code
 62 |  * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
 63 |  * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1, 2));
 64 |  * JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS,
 65 |  *     new CustomComparator(JSONCompareMode.LENIENT, customization));
 66 |  * }
 67 |  * 
 68 |  * 

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

69 | * 70 | *
{@code
 71 |  * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
 72 |  * Customization customization = new Customization("a", new ArrayValueMatcher(comparator));
 73 |  * JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS,
 74 |  *     new CustomComparator(JSONCompareMode.LENIENT, customization));
 75 |  * }
 76 |  * 
 77 |  * 

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:

78 | * 79 | *
{@code
 80 |  * // get length of array we will verify
 81 |  * int aLength = ((JSONArray)((JSONObject)JSONParser.parseJSON(ARRAY_OF_JSONOBJECTS)).get("a")).length();
 82 |  * // create array of customizations one for each array element
 83 |  * RegularExpressionValueMatcher regExValueMatcher =
 84 |  *     new RegularExpressionValueMatcher("\\d+");  // matches one or more digits
 85 |  * Customization[] customizations = new Customization[aLength];
 86 |  * for (int i=0; i regExArrayValueMatcher = new ArrayValueMatcher(regExComparator);
 92 |  * Customization regExArrayValueCustomization = new Customization("a", regExArrayValueMatcher);
 93 |  * CustomComparator regExCustomArrayValueComparator =
 94 |  *     new CustomComparator(JSONCompareMode.STRICT_ORDER, new Customization[] { regExArrayValueCustomization });
 95 |  * JSONAssert.assertEquals("{a:[{id:X}]}", ARRAY_OF_JSONOBJECTS, regExCustomArrayValueComparator);
 96 |  * }
 97 |  * 
 98 |  * 

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

99 | * 100 | *
{@code
101 |  * JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT);
102 |  * Customization customization = new Customization("a", new ArrayValueMatcher(comparator));
103 |  * JSONAssert.assertEquals("{a:[{background:white},{background:grey}]}", ARRAY_OF_JSONOBJECTS,
104 |  *     new CustomComparator(JSONCompareMode.LENIENT, customization));
105 |  * }
106 |  * 
107 |  * 

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

108 | * 109 | * {a:[[6,7,8], [9,10,11], [12,13,14], [19,20,21,22]]} 110 | * 111 | *

then:

112 | * 113 | *

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

114 | * 115 | *
{@code
116 |  * JSONComparator comparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER);
117 |  * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0, 2));
118 |  * JSONAssert.assertEquals("{a:[[3]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization));
119 |  * }
120 |  *
121 |  * 

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

122 | * 123 | *

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

124 | * 125 | *
{@code
126 |  * JSONComparator innerComparator = new DefaultComparator(JSONCompareMode.LENIENT);
127 |  * Customization innerCustomization = new Customization("a[1]", new ArrayValueMatcher(innerComparator, 0));
128 |  * JSONComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, innerCustomization);
129 |  * Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1));
130 |  * JSONAssert.assertEquals("{a:[[9]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization));
131 |  * }
132 |  *
133 |  * 

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

134 | * 135 | * @author Duncan Mackinder 136 | * @param Array Type 137 | */ 138 | public class ArrayValueMatcher implements LocationAwareValueMatcher { 139 | private final JSONComparator comparator; 140 | private final int from; 141 | private final int to; 142 | 143 | /** 144 | * Create ArrayValueMatcher to match every element in actual array against 145 | * elements taken in sequence from expected array, repeating from start of 146 | * expected array if necessary. 147 | * 148 | * @param comparator 149 | * comparator to use to compare elements 150 | */ 151 | public ArrayValueMatcher(JSONComparator comparator) { 152 | this(comparator, 0, Integer.MAX_VALUE); 153 | } 154 | 155 | /** 156 | * Create ArrayValueMatcher to match specified element in actual array 157 | * against first element of expected array. 158 | * 159 | * @param comparator 160 | * comparator to use to compare elements 161 | * @param index 162 | * index of the array element to be compared 163 | */ 164 | public ArrayValueMatcher(JSONComparator comparator, int index) { 165 | this(comparator, index, index); 166 | } 167 | 168 | /** 169 | * Create ArrayValueMatcher to match every element in specified range 170 | * (inclusive) from actual array against elements taken in sequence from 171 | * expected array, repeating from start of expected array if necessary. 172 | * 173 | * @param comparator 174 | * comparator to use to compare elements 175 | * @param from first element in actual array to compared 176 | * @param to last element in actual array to compared 177 | */ 178 | public ArrayValueMatcher(JSONComparator comparator, int from, int to) { 179 | assert comparator != null : "comparator null"; 180 | assert from >= 0 : MessageFormat.format("from({0}) < 0", from); 181 | assert to >= from : MessageFormat.format("to({0}) < from({1})", to, 182 | from); 183 | this.comparator = comparator; 184 | this.from = from; 185 | this.to = to; 186 | } 187 | 188 | @Override 189 | /* 190 | * NOTE: method defined as required by ValueMatcher interface but will never 191 | * be called so defined simply to indicate match failure 192 | */ 193 | public boolean equal(T o1, T o2) { 194 | return false; 195 | } 196 | 197 | @Override 198 | public boolean equal(String prefix, T actual, T expected, JSONCompareResult result) { 199 | if (!(actual instanceof JSONArray)) { 200 | throw new IllegalArgumentException("ArrayValueMatcher applied to non-array actual value"); 201 | } 202 | try { 203 | JSONArray actualArray = (JSONArray) actual; 204 | JSONArray expectedArray = expected instanceof JSONArray ? (JSONArray) expected: new JSONArray(new Object[] { expected }); 205 | int first = Math.max(0, from); 206 | int last = Math.min(actualArray.length() - 1, to); 207 | int expectedLen = expectedArray.length(); 208 | for (int i = first; i <= last; i++) { 209 | String elementPrefix = MessageFormat.format("{0}[{1}]", prefix, i); 210 | Object actualElement = actualArray.get(i); 211 | Object expectedElement = expectedArray.get((i - first) % expectedLen); 212 | comparator.compareValues(elementPrefix, expectedElement, actualElement, result); 213 | } 214 | // any failures have already been passed to result, so return true 215 | return true; 216 | } 217 | catch (JSONException e) { 218 | return false; 219 | } 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/Customization.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import java.util.regex.Pattern; 18 | 19 | /** 20 | * Associates a custom matcher to a specific jsonpath. 21 | */ 22 | public final class Customization { 23 | private final Pattern path; 24 | private final ValueMatcher comparator; 25 | 26 | public Customization(String path, ValueMatcher comparator) { 27 | assert path != null; 28 | assert comparator != null; 29 | this.path = Pattern.compile(buildPattern(path)); 30 | this.comparator = comparator; 31 | } 32 | 33 | private String buildPattern(String path) { 34 | return buildPatternLevel1(path); 35 | } 36 | 37 | private String buildPatternLevel1(String path) { 38 | String regex = "\\*\\*\\."; 39 | String replacement = "(?:.+\\.)?"; 40 | 41 | return buildPattern(path, regex, replacement, 1); 42 | } 43 | 44 | private String buildPatternLevel2(String s) { 45 | if (s.isEmpty()) { 46 | return ""; 47 | } 48 | String regex = "\\*\\*"; 49 | String replacement = ".+"; 50 | 51 | return buildPattern(s, regex, replacement, 2); 52 | } 53 | 54 | private String buildPatternLevel3(String s) { 55 | if (s.isEmpty()) { 56 | return ""; 57 | } 58 | 59 | String regex = "\\*"; 60 | String replacement = "[^\\.]+"; 61 | 62 | return buildPattern(s, regex, replacement, 3); 63 | } 64 | 65 | private String buildPattern(String path, String regex, String replacement, int level) { 66 | StringBuilder sb = new StringBuilder(); 67 | String[] parts = path.split(regex); 68 | for (int i = 0; i < parts.length; i++) { 69 | sb.append(buildPatternForLevel(level, parts[i])); 70 | if (i < parts.length - 1) { 71 | sb.append(replacement); 72 | } 73 | } 74 | return sb.toString(); 75 | } 76 | 77 | private String buildPatternForLevel(int level, String part) { 78 | switch (level) { 79 | case 1: 80 | return buildPatternLevel2(part); 81 | case 2: 82 | return buildPatternLevel3(part); 83 | case 3: 84 | return Pattern.quote(part); 85 | default: 86 | return "Incorrect level."; 87 | } 88 | } 89 | 90 | /** 91 | * Creates a new {@link Customization} instance for {@code path} and {@code comparator}. 92 | * 93 | * @param path the json path 94 | * @param comparator the comparator 95 | * @return a new Customization 96 | */ 97 | public static Customization customization(String path, ValueMatcher comparator) { 98 | return new Customization(path, comparator); 99 | } 100 | 101 | public boolean appliesToPath(String path) { 102 | return this.path.matcher(path).matches(); 103 | } 104 | 105 | /** 106 | * Return true if actual value matches expected value using this 107 | * Customization's comparator. Calls to this method should be replaced by 108 | * calls to matches(String prefix, Object actual, Object expected, 109 | * JSONCompareResult result). 110 | * 111 | * @param actual 112 | * JSON value being tested 113 | * @param expected 114 | * expected JSON value 115 | * @return true if actual value matches expected value 116 | */ 117 | @Deprecated 118 | public boolean matches(Object actual, Object expected) { 119 | return comparator.equal(actual, expected); 120 | } 121 | 122 | /** 123 | * Return true if actual value matches expected value using this 124 | * Customization's comparator. The equal method used for comparison depends 125 | * on type of comparator. 126 | * 127 | * @param prefix 128 | * JSON path of the JSON item being tested (only used if 129 | * comparator is a LocationAwareValueMatcher) 130 | * @param actual 131 | * JSON value being tested 132 | * @param expected 133 | * expected JSON value 134 | * @param result 135 | * JSONCompareResult to which match failure may be passed (only 136 | * used if comparator is a LocationAwareValueMatcher) 137 | * @return true if expected and actual equal or any difference has already 138 | * been passed to specified result instance, false otherwise. 139 | * @throws ValueMatcherException 140 | * if expected and actual values not equal and ValueMatcher 141 | * needs to override default comparison failure message that 142 | * would be generated if this method returned false. 143 | */ 144 | public boolean matches(String prefix, Object actual, Object expected, 145 | JSONCompareResult result) throws ValueMatcherException { 146 | if (comparator instanceof LocationAwareValueMatcher) { 147 | return ((LocationAwareValueMatcher)comparator).equal(prefix, actual, expected, result); 148 | } 149 | return comparator.equal(actual, expected); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/FieldComparisonFailure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | /** 18 | * Models a failure when comparing two fields. 19 | */ 20 | public class FieldComparisonFailure { 21 | private final String _field; 22 | private final Object _expected; 23 | private final Object _actual; 24 | 25 | public FieldComparisonFailure(String field, Object expected, Object actual) { 26 | this._field = field; 27 | this._expected = expected; 28 | this._actual = actual; 29 | } 30 | 31 | public String getField() { 32 | return _field; 33 | } 34 | 35 | public Object getExpected() { 36 | return _expected; 37 | } 38 | 39 | public Object getActual() { 40 | return _actual; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/JSONAssert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import org.json.JSONArray; 18 | import org.json.JSONObject; 19 | import org.skyscreamer.jsonassert.comparator.JSONComparator; 20 | 21 | /** 22 | *

A set of assertion methods useful for writing tests methods that return JSON.

23 | * 24 | *

There are two modes, strict and non-strict. In most cases, you will probably want 25 | * to set strict to false, since that will make the tests less brittle.

26 | * 27 | *

Strict tests require all of the elements requested to be returned, and only those elements 28 | * (ie, the tests are non-extensible). Arrays of elements must be returned in the same 29 | * order as expected. For example, say I'm expecting:

30 | * 31 | * {id:123,things['a','b','c']} 32 | * 33 | *

The following would match when doing non-strict checking, but would fail on strict checking:

34 | * 35 | * {id:123,things['c','b','a'],anotherfield:'blah'} 36 | * 37 | *

This library uses org.json. It has fewer dependencies than other JSON libraries (like net.sf.json), 38 | * making JSONassert more portable.

39 | * 40 | *

There are two known issues when dealing with non-strict comparisons:

41 | *
    42 | *
  • Unless the order is strict, checking does not handle mixed types in the JSONArray 43 | * (e.g. [1,2,{a:"b"}] or [{pet:"cat"},{car:"Ford"}])
  • 44 | *
  • Unless the order is strict, checking cannot handle arrays of arrays (e.g. [[1,2],[3,4]])
  • 45 | *
46 | *

You do not have to worry about encountering a false positive or false negative in these two edge cases. 47 | * JSONassert will identify the conditions and throw a descriptive {@link IllegalArgumentException}. These 48 | * cases will be fixed in future versions.

49 | * 50 | */ 51 | public class JSONAssert { 52 | private JSONAssert() {} 53 | 54 | /** 55 | * Asserts that the JSONObject provided matches the expected string. If it isn't it throws an 56 | * {@link AssertionError}. 57 | * 58 | * @param expectedStr Expected JSON string 59 | * @param actual JSONObject to compare 60 | * @param strict Enables strict checking 61 | */ 62 | public static void assertEquals(String expectedStr, JSONObject actual, boolean strict) { 63 | assertEquals(expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 64 | } 65 | 66 | /** 67 | * Asserts that the JSONObject provided matches the expected string. If it isn't it throws an 68 | * {@link AssertionError}. 69 | * 70 | * @param message Error message to be displayed in case of assertion failure 71 | * @param expectedStr Expected JSON string 72 | * @param actual JSONObject to compare 73 | * @param strict Enables strict checking 74 | */ 75 | public static void assertEquals(String message, String expectedStr, JSONObject actual, boolean strict) { 76 | assertEquals(message, expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 77 | } 78 | 79 | /** 80 | * Asserts that the JSONObject provided does not match the expected string. If it is it throws an 81 | * {@link AssertionError}. 82 | * 83 | * @see #assertEquals(String, JSONObject, boolean) 84 | * 85 | * @param expectedStr Expected JSON string 86 | * @param actual JSONObject to compare 87 | * @param strict Enables strict checking 88 | */ 89 | public static void assertNotEquals(String expectedStr, JSONObject actual, boolean strict) { 90 | assertNotEquals(expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 91 | } 92 | 93 | /** 94 | * Asserts that the JSONObject provided does not match the expected string. If it is it throws an 95 | * {@link AssertionError}. 96 | * 97 | * @see #assertEquals(String, JSONObject, boolean) 98 | * 99 | * @param message Error message to be displayed in case of assertion failure 100 | * @param expectedStr Expected JSON string 101 | * @param actual JSONObject to compare 102 | * @param strict Enables strict checking 103 | */ 104 | public static void assertNotEquals(String message, String expectedStr, JSONObject actual, boolean strict) { 105 | assertNotEquals(message, expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 106 | } 107 | 108 | /** 109 | * Asserts that the JSONObject provided matches the expected string. If it isn't it throws an 110 | * {@link AssertionError}. 111 | * 112 | * @param expectedStr Expected JSON string 113 | * @param actual JSONObject to compare 114 | * @param compareMode Specifies which comparison mode to use 115 | */ 116 | public static void assertEquals(String expectedStr, JSONObject actual, JSONCompareMode compareMode) { 117 | assertEquals("", expectedStr, actual, compareMode); 118 | } 119 | 120 | /** 121 | * Asserts that the JSONObject provided matches the expected string. If it isn't it throws an 122 | * {@link AssertionError}. 123 | * 124 | * @param message Error message to be displayed in case of assertion failure 125 | * @param expectedStr Expected JSON string 126 | * @param actual JSONObject to compare 127 | * @param compareMode Specifies which comparison mode to use 128 | */ 129 | public static void assertEquals(String message, String expectedStr, JSONObject actual, JSONCompareMode compareMode) 130 | { 131 | Object expected = JSONParser.parseJSON(expectedStr); 132 | if (expected instanceof JSONObject) { 133 | assertEquals(message, (JSONObject)expected, actual, compareMode); 134 | } 135 | else { 136 | throw new AssertionError("Expecting a JSON array, but passing in a JSON object"); 137 | } 138 | } 139 | 140 | /** 141 | * Asserts that the JSONObject provided does not match the expected string. If it is it throws an 142 | * {@link AssertionError}. 143 | * 144 | * @see #assertEquals(String, JSONObject, JSONCompareMode) 145 | * 146 | * @param expectedStr Expected JSON string 147 | * @param actual JSONObject to compare 148 | * @param compareMode Specifies which comparison mode to use 149 | */ 150 | public static void assertNotEquals(String expectedStr, JSONObject actual, JSONCompareMode compareMode) { 151 | assertNotEquals("", expectedStr, actual, compareMode); 152 | } 153 | 154 | /** 155 | * Asserts that the JSONObject provided does not match the expected string. If it is it throws an 156 | * {@link AssertionError}. 157 | * 158 | * @see #assertEquals(String, JSONObject, JSONCompareMode) 159 | * 160 | * @param message Error message to be displayed in case of assertion failure 161 | * @param expectedStr Expected JSON string 162 | * @param actual JSONObject to compare 163 | * @param compareMode Specifies which comparison mode to use 164 | */ 165 | public static void assertNotEquals(String message, String expectedStr, JSONObject actual, 166 | JSONCompareMode compareMode) { 167 | Object expected = JSONParser.parseJSON(expectedStr); 168 | if (expected instanceof JSONObject) { 169 | assertNotEquals(message, (JSONObject) expected, actual, compareMode); 170 | } 171 | else { 172 | throw new AssertionError("Expecting a JSON array, but passing in a JSON object"); 173 | } 174 | } 175 | 176 | /** 177 | * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an 178 | * {@link AssertionError}. 179 | * 180 | * @param expectedStr Expected JSON string 181 | * @param actual JSONArray to compare 182 | * @param strict Enables strict checking 183 | */ 184 | public static void assertEquals(String expectedStr, JSONArray actual, boolean strict) { 185 | assertEquals(expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 186 | } 187 | 188 | /** 189 | * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an 190 | * {@link AssertionError}. 191 | * 192 | * @param message Error message to be displayed in case of assertion failure 193 | * @param expectedStr Expected JSON string 194 | * @param actual JSONArray to compare 195 | * @param strict Enables strict checking 196 | */ 197 | public static void assertEquals(String message, String expectedStr, JSONArray actual, boolean strict) { 198 | assertEquals(message, expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 199 | } 200 | 201 | /** 202 | * Asserts that the JSONArray provided does not match the expected string. If it is it throws an 203 | * {@link AssertionError}. 204 | * 205 | * @param expectedStr Expected JSON string 206 | * @param actual JSONArray to compare 207 | * @param strict Enables strict checking 208 | */ 209 | public static void assertNotEquals(String expectedStr, JSONArray actual, boolean strict) { 210 | assertNotEquals(expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 211 | } 212 | 213 | /** 214 | * Asserts that the JSONArray provided does not match the expected string. If it is it throws an 215 | * {@link AssertionError}. 216 | * 217 | * @param message Error message to be displayed in case of assertion failure 218 | * @param expectedStr Expected JSON string 219 | * @param actual JSONArray to compare 220 | * @param strict Enables strict checking 221 | */ 222 | public static void assertNotEquals(String message, String expectedStr, JSONArray actual, boolean strict) { 223 | assertNotEquals(message, expectedStr, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 224 | } 225 | 226 | /** 227 | * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an 228 | * {@link AssertionError}. 229 | * 230 | * @param expectedStr Expected JSON string 231 | * @param actual JSONArray to compare 232 | * @param compareMode Specifies which comparison mode to use 233 | */ 234 | public static void assertEquals(String expectedStr, JSONArray actual, JSONCompareMode compareMode) { 235 | assertEquals("", expectedStr, actual, compareMode); 236 | } 237 | 238 | /** 239 | * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an 240 | * {@link AssertionError}. 241 | * 242 | * @param message Error message to be displayed in case of assertion failure 243 | * @param expectedStr Expected JSON string 244 | * @param actual JSONArray to compare 245 | * @param compareMode Specifies which comparison mode to use 246 | */ 247 | public static void assertEquals(String message, String expectedStr, JSONArray actual, JSONCompareMode compareMode) { 248 | Object expected = JSONParser.parseJSON(expectedStr); 249 | if (expected instanceof JSONArray) { 250 | assertEquals(message, (JSONArray) expected, actual, compareMode); 251 | } 252 | else { 253 | throw new AssertionError("Expecting a JSON object, but passing in a JSON array"); 254 | } 255 | } 256 | 257 | /** 258 | * Asserts that the JSONArray provided does not match the expected string. If it is it throws an 259 | * {@link AssertionError}. 260 | * 261 | * @param expectedStr Expected JSON string 262 | * @param actual JSONArray to compare 263 | * @param compareMode Specifies which comparison mode to use 264 | */ 265 | public static void assertNotEquals(String expectedStr, JSONArray actual, JSONCompareMode compareMode) { 266 | Object expected = JSONParser.parseJSON(expectedStr); 267 | if (expected instanceof JSONArray) { 268 | assertNotEquals((JSONArray) expected, actual, compareMode); 269 | } 270 | else { 271 | throw new AssertionError("Expecting a JSON object, but passing in a JSON array"); 272 | } 273 | } 274 | 275 | /** 276 | * Asserts that the JSONArray provided does not match the expected string. If it is it throws an 277 | * {@link AssertionError}. 278 | * 279 | * @param message Error message to be displayed in case of assertion failure 280 | * @param expectedStr Expected JSON string 281 | * @param actual JSONArray to compare 282 | * @param compareMode Specifies which comparison mode to use 283 | */ 284 | public static void assertNotEquals(String message, String expectedStr, JSONArray actual, 285 | JSONCompareMode compareMode) { 286 | Object expected = JSONParser.parseJSON(expectedStr); 287 | if (expected instanceof JSONArray) { 288 | assertNotEquals(message, (JSONArray) expected, actual, compareMode); 289 | } 290 | else { 291 | throw new AssertionError("Expecting a JSON object, but passing in a JSON array"); 292 | } 293 | } 294 | 295 | /** 296 | * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an 297 | * {@link AssertionError}. 298 | * 299 | * @param expectedStr Expected JSON string 300 | * @param actualStr String to compare 301 | * @param strict Enables strict checking 302 | */ 303 | public static void assertEquals(String expectedStr, String actualStr, boolean strict) { 304 | assertEquals(expectedStr, actualStr, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 305 | } 306 | 307 | /** 308 | * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an 309 | * {@link AssertionError}. 310 | * 311 | * @param message Error message to be displayed in case of assertion failure 312 | * @param expectedStr Expected JSON string 313 | * @param actualStr String to compare 314 | * @param strict Enables strict checking 315 | */ 316 | public static void assertEquals(String message, String expectedStr, String actualStr, boolean strict) { 317 | assertEquals(message, expectedStr, actualStr, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 318 | } 319 | 320 | /** 321 | * Asserts that the JSONArray provided does not match the expected string. If it is it throws an 322 | * {@link AssertionError}. 323 | * 324 | * @param expectedStr Expected JSON string 325 | * @param actualStr String to compare 326 | * @param strict Enables strict checking 327 | */ 328 | public static void assertNotEquals(String expectedStr, String actualStr, boolean strict) { 329 | assertNotEquals(expectedStr, actualStr, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 330 | } 331 | 332 | /** 333 | * Asserts that the JSONArray provided does not match the expected string. If it is it throws an 334 | * {@link AssertionError}. 335 | * 336 | * @param message Error message to be displayed in case of assertion failure 337 | * @param expectedStr Expected JSON string 338 | * @param actualStr String to compare 339 | * @param strict Enables strict checking 340 | */ 341 | public static void assertNotEquals(String message, String expectedStr, String actualStr, boolean strict) { 342 | assertNotEquals(message, expectedStr, actualStr, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 343 | } 344 | 345 | /** 346 | * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an 347 | * {@link AssertionError}. 348 | * 349 | * @param expectedStr Expected JSON string 350 | * @param actualStr String to compare 351 | * @param compareMode Specifies which comparison mode to use 352 | */ 353 | public static void assertEquals(String expectedStr, String actualStr, JSONCompareMode compareMode) { 354 | assertEquals("", expectedStr, actualStr, compareMode); 355 | } 356 | 357 | /** 358 | * Asserts that the JSONArray provided matches the expected string. If it isn't it throws an 359 | * {@link AssertionError}. 360 | * 361 | * @param message Error message to be displayed in case of assertion failure 362 | * @param expectedStr Expected JSON string 363 | * @param actualStr String to compare 364 | * @param compareMode Specifies which comparison mode to use 365 | */ 366 | public static void assertEquals(String message, String expectedStr, String actualStr, JSONCompareMode compareMode) { 367 | if (expectedStr==actualStr) return; 368 | if (expectedStr==null){ 369 | throw new AssertionError("Expected string is null."); 370 | }else if (actualStr==null){ 371 | throw new AssertionError("Actual string is null."); 372 | } 373 | JSONCompareResult result = JSONCompare.compareJSON(expectedStr, actualStr, compareMode); 374 | if (result.failed()) { 375 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 376 | } 377 | } 378 | 379 | /** 380 | * Asserts that the JSONArray provided does not match the expected string. If it is it throws an 381 | * {@link AssertionError}. 382 | * 383 | * @param expectedStr Expected JSON string 384 | * @param actualStr String to compare 385 | * @param compareMode Specifies which comparison mode to use 386 | */ 387 | public static void assertNotEquals(String expectedStr, String actualStr, JSONCompareMode compareMode) { 388 | assertNotEquals("", expectedStr, actualStr, compareMode); 389 | } 390 | 391 | /** 392 | * Asserts that the JSONArray provided does not match the expected string. If it is it throws an 393 | * {@link AssertionError}. 394 | * 395 | * @param message Error message to be displayed in case of assertion failure 396 | * @param expectedStr Expected JSON string 397 | * @param actualStr String to compare 398 | * @param compareMode Specifies which comparison mode to use 399 | */ 400 | public static void assertNotEquals(String message, String expectedStr, String actualStr, 401 | JSONCompareMode compareMode) { 402 | JSONCompareResult result = JSONCompare.compareJSON(expectedStr, actualStr, compareMode); 403 | if (result.passed()) { 404 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 405 | } 406 | } 407 | 408 | /** 409 | * Asserts that the json string provided matches the expected string. If it isn't it throws an 410 | * {@link AssertionError}. 411 | * 412 | * @param expectedStr Expected JSON string 413 | * @param actualStr String to compare 414 | * @param comparator Comparator 415 | */ 416 | public static void assertEquals(String expectedStr, String actualStr, JSONComparator comparator) { 417 | assertEquals("", expectedStr, actualStr, comparator); 418 | 419 | } 420 | 421 | /** 422 | * Asserts that the json string provided matches the expected string. If it isn't it throws an 423 | * {@link AssertionError}. 424 | * 425 | * @param message Error message to be displayed in case of assertion failure 426 | * @param expectedStr Expected JSON string 427 | * @param actualStr String to compare 428 | * @param comparator Comparator 429 | */ 430 | public static void assertEquals(String message, String expectedStr, String actualStr, JSONComparator comparator) { 431 | JSONCompareResult result = JSONCompare.compareJSON(expectedStr, actualStr, comparator); 432 | if (result.failed()) { 433 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 434 | } 435 | } 436 | 437 | /** 438 | * Asserts that the json string provided does not match the expected string. If it is it throws an 439 | * {@link AssertionError}. 440 | * 441 | * @param expectedStr Expected JSON string 442 | * @param actualStr String to compare 443 | * @param comparator Comparator 444 | */ 445 | public static void assertNotEquals(String expectedStr, String actualStr, JSONComparator comparator) { 446 | assertNotEquals("", expectedStr, actualStr, comparator); 447 | } 448 | 449 | /** 450 | * Asserts that the json string provided does not match the expected string. If it is it throws an 451 | * {@link AssertionError}. 452 | * 453 | * @param message Error message to be displayed in case of assertion failure 454 | * @param expectedStr Expected JSON string 455 | * @param actualStr String to compare 456 | * @param comparator Comparator 457 | */ 458 | public static void assertNotEquals(String message, String expectedStr, String actualStr, 459 | JSONComparator comparator) { 460 | JSONCompareResult result = JSONCompare.compareJSON(expectedStr, actualStr, comparator); 461 | if (result.passed()) { 462 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 463 | } 464 | } 465 | 466 | /** 467 | * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an 468 | * {@link AssertionError}. 469 | * 470 | * @param expected Expected JSONObject 471 | * @param actual JSONObject to compare 472 | * @param comparator Comparator 473 | */ 474 | public static void assertEquals(JSONObject expected, JSONObject actual, JSONComparator comparator) { 475 | assertEquals("", expected, actual, comparator); 476 | } 477 | 478 | /** 479 | * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an 480 | * {@link AssertionError}. 481 | * 482 | * @param message Error message to be displayed in case of assertion failure 483 | * @param expected Expected JSONObject 484 | * @param actual JSONObject to compare 485 | * @param comparator Comparator 486 | */ 487 | public static void assertEquals(String message, JSONObject expected, JSONObject actual, JSONComparator comparator) { 488 | JSONCompareResult result = JSONCompare.compareJSON(expected, actual, comparator); 489 | if (result.failed()) { 490 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 491 | } 492 | } 493 | 494 | /** 495 | * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an 496 | * {@link AssertionError}. 497 | * 498 | * @param expected Expected JSONObject 499 | * @param actual JSONObject to compare 500 | * @param comparator Comparator 501 | */ 502 | public static void assertNotEquals(JSONObject expected, JSONObject actual, JSONComparator comparator) { 503 | assertNotEquals("", expected, actual, comparator); 504 | } 505 | 506 | /** 507 | * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an 508 | * {@link AssertionError}. 509 | * 510 | * @param message Error message to be displayed in case of assertion failure 511 | * @param expected Expected JSONObject 512 | * @param actual JSONObject to compare 513 | * @param comparator Comparator 514 | */ 515 | public static void assertNotEquals(String message, JSONObject expected, JSONObject actual, 516 | JSONComparator comparator) { 517 | JSONCompareResult result = JSONCompare.compareJSON(expected, actual, comparator); 518 | if (result.passed()) { 519 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 520 | } 521 | } 522 | 523 | /** 524 | * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an 525 | * {@link AssertionError}. 526 | * 527 | * @param expected Expected JSONObject 528 | * @param actual JSONObject to compare 529 | * @param strict Enables strict checking 530 | */ 531 | public static void assertEquals(JSONObject expected, JSONObject actual, boolean strict) { 532 | assertEquals(expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 533 | } 534 | 535 | /** 536 | * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an 537 | * {@link AssertionError}. 538 | * 539 | * @param message Error message to be displayed in case of assertion failure 540 | * @param expected Expected JSONObject 541 | * @param actual JSONObject to compare 542 | * @param strict Enables strict checking 543 | */ 544 | public static void assertEquals(String message, JSONObject expected, JSONObject actual, boolean strict) { 545 | assertEquals(message, expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 546 | } 547 | 548 | /** 549 | * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an 550 | * {@link AssertionError}. 551 | * 552 | * @param expected Expected JSONObject 553 | * @param actual JSONObject to compare 554 | * @param strict Enables strict checking 555 | */ 556 | public static void assertNotEquals(JSONObject expected, JSONObject actual, boolean strict) { 557 | assertNotEquals(expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 558 | } 559 | 560 | /** 561 | * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an 562 | * {@link AssertionError}. 563 | * 564 | * @param message Error message to be displayed in case of assertion failure 565 | * @param expected Expected JSONObject 566 | * @param actual JSONObject to compare 567 | * @param strict Enables strict checking 568 | */ 569 | public static void assertNotEquals(String message, JSONObject expected, JSONObject actual, boolean strict) { 570 | assertNotEquals(message, expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 571 | } 572 | 573 | /** 574 | * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an 575 | * {@link AssertionError}. 576 | * 577 | * @param expected Expected JSONObject 578 | * @param actual JSONObject to compare 579 | * @param compareMode Specifies which comparison mode to use 580 | */ 581 | public static void assertEquals(JSONObject expected, JSONObject actual, JSONCompareMode compareMode) { 582 | assertEquals("", expected, actual, compareMode); 583 | } 584 | 585 | /** 586 | * Asserts that the JSONObject provided matches the expected JSONObject. If it isn't it throws an 587 | * {@link AssertionError}. 588 | * 589 | * @param message Error message to be displayed in case of assertion failure 590 | * @param expected Expected JSONObject 591 | * @param actual JSONObject to compare 592 | * @param compareMode Specifies which comparison mode to use 593 | */ 594 | public static void assertEquals(String message, JSONObject expected, JSONObject actual, 595 | JSONCompareMode compareMode) { 596 | JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); 597 | if (result.failed()) { 598 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 599 | } 600 | } 601 | 602 | /** 603 | * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an 604 | * {@link AssertionError}. 605 | * 606 | * @param expected Expected JSONObject 607 | * @param actual JSONObject to compare 608 | * @param compareMode Specifies which comparison mode to use 609 | */ 610 | public static void assertNotEquals(JSONObject expected, JSONObject actual, JSONCompareMode compareMode) { 611 | assertNotEquals("", expected, actual, compareMode); 612 | } 613 | 614 | /** 615 | * Asserts that the JSONObject provided does not match the expected JSONObject. If it is it throws an 616 | * {@link AssertionError}. 617 | * 618 | * @param message Error message to be displayed in case of assertion failure 619 | * @param expected Expected JSONObject 620 | * @param actual JSONObject to compare 621 | * @param compareMode Specifies which comparison mode to use 622 | */ 623 | public static void assertNotEquals(String message, JSONObject expected, JSONObject actual, 624 | JSONCompareMode compareMode) { 625 | JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); 626 | if (result.passed()) { 627 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 628 | } 629 | } 630 | 631 | /** 632 | * Asserts that the JSONArray provided matches the expected JSONArray. If it isn't it throws an 633 | * {@link AssertionError}. 634 | * 635 | * @param expected Expected JSONArray 636 | * @param actual JSONArray to compare 637 | * @param strict Enables strict checking 638 | */ 639 | public static void assertEquals(JSONArray expected, JSONArray actual, boolean strict) { 640 | assertEquals("", expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 641 | } 642 | 643 | /** 644 | * Asserts that the JSONArray provided matches the expected JSONArray. If it isn't it throws an 645 | * {@link AssertionError}. 646 | * 647 | * @param message Error message to be displayed in case of assertion failure 648 | * @param expected Expected JSONArray 649 | * @param actual JSONArray to compare 650 | * @param strict Enables strict checking 651 | */ 652 | public static void assertEquals(String message, JSONArray expected, JSONArray actual, boolean strict) { 653 | assertEquals(message, expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 654 | } 655 | 656 | /** 657 | * Asserts that the JSONArray provided does not match the expected JSONArray. If it is it throws an 658 | * {@link AssertionError}. 659 | * 660 | * @param expected Expected JSONArray 661 | * @param actual JSONArray to compare 662 | * @param strict Enables strict checking 663 | */ 664 | public static void assertNotEquals(JSONArray expected, JSONArray actual, boolean strict) { 665 | assertNotEquals(expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 666 | } 667 | 668 | /** 669 | * Asserts that the JSONArray provided does not match the expected JSONArray. If it is it throws an 670 | * {@link AssertionError}. 671 | * 672 | * @param message Error message to be displayed in case of assertion failure 673 | * @param expected Expected JSONArray 674 | * @param actual JSONArray to compare 675 | * @param strict Enables strict checking 676 | */ 677 | public static void assertNotEquals(String message, JSONArray expected, JSONArray actual, boolean strict) { 678 | assertNotEquals(message, expected, actual, strict ? JSONCompareMode.STRICT : JSONCompareMode.LENIENT); 679 | } 680 | 681 | /** 682 | * Asserts that the JSONArray provided matches the expected JSONArray. If it isn't it throws an 683 | * {@link AssertionError}. 684 | * 685 | * @param expected Expected JSONArray 686 | * @param actual JSONArray to compare 687 | * @param compareMode Specifies which comparison mode to use 688 | */ 689 | public static void assertEquals(JSONArray expected, JSONArray actual, JSONCompareMode compareMode) { 690 | assertEquals("", expected, actual, compareMode); 691 | } 692 | 693 | /** 694 | * Asserts that the JSONArray provided matches the expected JSONArray. If it isn't it throws an 695 | * {@link AssertionError}. 696 | * 697 | * @param message Error message to be displayed in case of assertion failure 698 | * @param expected Expected JSONArray 699 | * @param actual JSONArray to compare 700 | * @param compareMode Specifies which comparison mode to use 701 | */ 702 | public static void assertEquals(String message, JSONArray expected, JSONArray actual, JSONCompareMode compareMode) { 703 | JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); 704 | if (result.failed()) { 705 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 706 | } 707 | } 708 | 709 | /** 710 | * Asserts that the JSONArray provided does not match the expected JSONArray. If it is it throws an 711 | * {@link AssertionError}. 712 | * 713 | * @param expected Expected JSONArray 714 | * @param actual JSONArray to compare 715 | * @param compareMode Specifies which comparison mode to use 716 | */ 717 | public static void assertNotEquals(JSONArray expected, JSONArray actual, JSONCompareMode compareMode) { 718 | assertNotEquals("", expected, actual, compareMode); 719 | } 720 | 721 | /** 722 | * Asserts that the JSONArray provided does not match the expected JSONArray. If it is it throws an 723 | * {@link AssertionError}. 724 | * 725 | * @param message Error message to be displayed in case of assertion failure 726 | * @param expected Expected JSONArray 727 | * @param actual JSONArray to compare 728 | * @param compareMode Specifies which comparison mode to use 729 | */ 730 | public static void assertNotEquals(String message, JSONArray expected, JSONArray actual, 731 | JSONCompareMode compareMode) { 732 | JSONCompareResult result = JSONCompare.compareJSON(expected, actual, compareMode); 733 | if (result.passed()) { 734 | throw new AssertionError(getCombinedMessage(message, result.getMessage())); 735 | } 736 | } 737 | 738 | private static String getCombinedMessage(String message1, String message2) { 739 | String combinedMessage = ""; 740 | 741 | if(message1 == null || "".equals(message1)) { 742 | combinedMessage = message2; 743 | } else { 744 | combinedMessage = message1 + " " + message2; 745 | } 746 | return combinedMessage; 747 | } 748 | } 749 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/JSONCompare.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import org.json.JSONArray; 18 | import org.json.JSONObject; 19 | import org.json.JSONString; 20 | import org.skyscreamer.jsonassert.comparator.DefaultComparator; 21 | import org.skyscreamer.jsonassert.comparator.JSONComparator; 22 | 23 | /** 24 | * Provides API to compare two JSON entities. This is the backend to {@link JSONAssert}, but it can 25 | * be programmed against directly to access the functionality. (eg, to make something that works with a 26 | * non-JUnit test framework) 27 | */ 28 | public final class JSONCompare { 29 | private JSONCompare() { 30 | } 31 | 32 | private static JSONComparator getComparatorForMode(JSONCompareMode mode) { 33 | return new DefaultComparator(mode); 34 | } 35 | 36 | /** 37 | * Compares JSON string provided to the expected JSON string using provided comparator, and returns the results of 38 | * the comparison. 39 | * @param expectedStr Expected JSON string 40 | * @param actualStr JSON string to compare 41 | * @param comparator Comparator to use 42 | * @return result of the comparison 43 | * @throws IllegalArgumentException when type of expectedStr doesn't match the type of actualStr 44 | */ 45 | public static JSONCompareResult compareJSON(String expectedStr, String actualStr, JSONComparator comparator) { 46 | Object expected = JSONParser.parseJSON(expectedStr); 47 | Object actual = JSONParser.parseJSON(actualStr); 48 | if ((expected instanceof JSONObject) && (actual instanceof JSONObject)) { 49 | return compareJSON((JSONObject) expected, (JSONObject) actual, comparator); 50 | } 51 | else if ((expected instanceof JSONArray) && (actual instanceof JSONArray)) { 52 | return compareJSON((JSONArray)expected, (JSONArray)actual, comparator); 53 | } 54 | else if (expected instanceof JSONString && actual instanceof JSONString) { 55 | return compareJson((JSONString) expected, (JSONString) actual); 56 | } 57 | else if (expected instanceof JSONObject) { 58 | return new JSONCompareResult().fail("", expected, actual); 59 | } 60 | else { 61 | return new JSONCompareResult().fail("", expected, actual); 62 | } 63 | } 64 | 65 | /** 66 | * Compares JSON object provided to the expected JSON object using provided comparator, and returns the results of 67 | * the comparison. 68 | * @param expected expected json object 69 | * @param actual actual json object 70 | * @param comparator comparator to use 71 | * @return result of the comparison 72 | */ 73 | public static JSONCompareResult compareJSON(JSONObject expected, JSONObject actual, JSONComparator comparator) { 74 | return comparator.compareJSON(expected, actual); 75 | } 76 | 77 | /** 78 | * Compares JSON object provided to the expected JSON object using provided comparator, and returns the results of 79 | * the comparison. 80 | * @param expected expected json array 81 | * @param actual actual json array 82 | * @param comparator comparator to use 83 | * @return result of the comparison 84 | */ 85 | public static JSONCompareResult compareJSON(JSONArray expected, JSONArray actual, JSONComparator comparator) { 86 | return comparator.compareJSON(expected, actual); 87 | } 88 | 89 | /** 90 | * Compares {@link JSONString} provided to the expected {@code JSONString}, checking that the 91 | * {@link org.json.JSONString#toJSONString()} are equal. 92 | * 93 | * @param expected Expected {@code JSONstring} 94 | * @param actual {@code JSONstring} to compare 95 | * @return result of the comparison 96 | */ 97 | public static JSONCompareResult compareJson(final JSONString expected, final JSONString actual) { 98 | final JSONCompareResult result = new JSONCompareResult(); 99 | final String expectedJson = expected.toJSONString(); 100 | final String actualJson = actual.toJSONString(); 101 | if (!expectedJson.equals(actualJson)) { 102 | result.fail(""); 103 | } 104 | return result; 105 | } 106 | 107 | /** 108 | * Compares JSON string provided to the expected JSON string, and returns the results of the comparison. 109 | * 110 | * @param expectedStr Expected JSON string 111 | * @param actualStr JSON string to compare 112 | * @param mode Defines comparison behavior 113 | * @return result of the comparison 114 | */ 115 | public static JSONCompareResult compareJSON(String expectedStr, String actualStr, JSONCompareMode mode) { 116 | return compareJSON(expectedStr, actualStr, getComparatorForMode(mode)); 117 | } 118 | 119 | /** 120 | * Compares JSONObject provided to the expected JSONObject, and returns the results of the comparison. 121 | * 122 | * @param expected Expected JSONObject 123 | * @param actual JSONObject to compare 124 | * @param mode Defines comparison behavior 125 | * @return result of the comparison 126 | */ 127 | public static JSONCompareResult compareJSON(JSONObject expected, JSONObject actual, JSONCompareMode mode) { 128 | return compareJSON(expected, actual, getComparatorForMode(mode)); 129 | } 130 | 131 | 132 | /** 133 | * Compares JSONArray provided to the expected JSONArray, and returns the results of the comparison. 134 | * 135 | * @param expected Expected JSONArray 136 | * @param actual JSONArray to compare 137 | * @param mode Defines comparison behavior 138 | * @return result of the comparison 139 | */ 140 | public static JSONCompareResult compareJSON(JSONArray expected, JSONArray actual, JSONCompareMode mode) { 141 | return compareJSON(expected, actual, getComparatorForMode(mode)); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/JSONCompareMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | /** 18 | *

These different modes define different behavior for the comparison of JSON for testing. 19 | * Each mode encapsulates two underlying behaviors: extensibility and strict ordering.

20 | * 21 | * 22 | * 25 | * 26 | * 27 | * 28 | * 29 | * 30 | *
23 | * Behavior of JSONCompareMode 24 | *
 ExtensibleStrict Ordering
STRICTnoyes
LENIENTyesno
NON_EXTENSIBLEnono
STRICT_ORDERyesyes
31 | * 32 | *

If extensibility not allowed, then all of the expected values must match in what's being tested, 33 | * but any additional fields will cause the test to fail. When extensibility is allowed, all values 34 | * must still match. For example, if you're expecting:

35 | * 36 | * {id:1,name:"Carter"} 37 | * 38 | *

Then the following will pass when extensible, and will fail when not:

39 | * 40 | * {id:1,name:"Carter",favoriteColor:"blue"} 41 | * 42 | *

If strict ordering is enabled, JSON arrays must be in strict sequence. For example, if you're expecting:

43 | * 44 | * {id:1,friends:[{id:2},{id:3}]} 45 | * 46 | *

Then the following will fail strict ordering, but will otherwise pass:

47 | * 48 | * {id:1,friends:[{id:3},{id:2}]} 49 | * 50 | */ 51 | public enum JSONCompareMode { 52 | /** 53 | * Strict checking. Not extensible, and strict array ordering. 54 | */ 55 | STRICT(false, true), 56 | /** 57 | * Lenient checking. Extensible, and non-strict array ordering. 58 | */ 59 | LENIENT(true, false), 60 | /** 61 | * Non-extensible checking. Not extensible, and non-strict array ordering. 62 | */ 63 | NON_EXTENSIBLE(false, false), 64 | /** 65 | * Strict order checking. Extensible, and strict array ordering. 66 | */ 67 | STRICT_ORDER(true, true); 68 | 69 | private final boolean _extensible; 70 | private final boolean _strictOrder; 71 | 72 | JSONCompareMode(boolean extensible, boolean strictOrder) { 73 | _extensible = extensible; 74 | _strictOrder = strictOrder; 75 | } 76 | 77 | /** 78 | * Is extensible 79 | * @return True if results can be extended from what's expected, otherwise false. 80 | */ 81 | public boolean isExtensible() { 82 | return _extensible; 83 | } 84 | 85 | /** 86 | * Strict order required 87 | * @return True if results require strict array ordering, otherwise false. 88 | */ 89 | public boolean hasStrictOrder() { 90 | return _strictOrder; 91 | } 92 | 93 | /** 94 | * Get the equivalent {@code JSONCompareMode} with or without strict ordering. 95 | * 96 | * @param strictOrdering if true, requires strict ordering of array elements 97 | * @return the equivalent {@code JSONCompareMode} 98 | */ 99 | public JSONCompareMode withStrictOrdering(boolean strictOrdering) { 100 | if (strictOrdering) { 101 | return isExtensible() ? STRICT_ORDER : STRICT; 102 | } else { 103 | return isExtensible() ? LENIENT : NON_EXTENSIBLE; 104 | } 105 | } 106 | 107 | /** 108 | * Get the equivalent {@code JSONCompareMode} with or without extensibility. 109 | * 110 | * @param extensible if true, allows keys in actual that don't appear in expected 111 | * @return the equivalent {@code JSONCompareMode} 112 | */ 113 | public JSONCompareMode withExtensible(boolean extensible) { 114 | if (extensible) { 115 | return hasStrictOrder() ? STRICT_ORDER : LENIENT; 116 | } else { 117 | return hasStrictOrder() ? STRICT : NON_EXTENSIBLE; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/JSONCompareResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Collections; 19 | import java.util.List; 20 | 21 | import org.json.JSONArray; 22 | import org.json.JSONObject; 23 | 24 | /** 25 | * Bean for holding results from JSONCompare. 26 | */ 27 | public class JSONCompareResult { 28 | private boolean _success; 29 | private StringBuilder _message; 30 | private String _field; 31 | private Object _expected; 32 | private Object _actual; 33 | private final List _fieldFailures = new ArrayList(); 34 | private final List _fieldMissing = new ArrayList(); 35 | private final List _fieldUnexpected = new ArrayList(); 36 | 37 | /** 38 | * Default constructor. 39 | */ 40 | public JSONCompareResult() { 41 | this(true, null); 42 | } 43 | 44 | private JSONCompareResult(boolean success, String message) { 45 | _success = success; 46 | _message = new StringBuilder(message == null ? "" : message); 47 | } 48 | 49 | /** 50 | * Did the comparison pass? 51 | * @return True if it passed 52 | */ 53 | public boolean passed() { 54 | return _success; 55 | } 56 | 57 | /** 58 | * Did the comparison fail? 59 | * @return True if it failed 60 | */ 61 | public boolean failed() { 62 | return !_success; 63 | } 64 | 65 | /** 66 | * Result message 67 | * @return String explaining why if the comparison failed 68 | */ 69 | public String getMessage() { 70 | return _message.toString(); 71 | } 72 | 73 | /** 74 | * Get the list of failures on field comparisons 75 | * @return list of comparsion failures 76 | */ 77 | public List getFieldFailures() { 78 | return Collections.unmodifiableList(_fieldFailures); 79 | } 80 | 81 | /** 82 | * Get the list of missed on field comparisons 83 | * @return list of comparsion failures 84 | */ 85 | public List getFieldMissing() { 86 | return Collections.unmodifiableList(_fieldMissing); 87 | } 88 | 89 | /** 90 | * Get the list of failures on field comparisons 91 | * @return list of comparsion failures 92 | */ 93 | public List getFieldUnexpected() { 94 | return Collections.unmodifiableList(_fieldUnexpected); 95 | } 96 | 97 | /** 98 | * Actual field value 99 | * 100 | * @return a {@code JSONObject}, {@code JSONArray} or other {@code Object} 101 | * instance, or {@code null} if the comparison did not fail on a 102 | * particular field 103 | * @deprecated Superseded by {@link #getFieldFailures()} 104 | */ 105 | @Deprecated 106 | public Object getActual() { 107 | return _actual; 108 | } 109 | 110 | /** 111 | * Expected field value 112 | * 113 | * @return a {@code JSONObject}, {@code JSONArray} or other {@code Object} 114 | * instance, or {@code null} if the comparison did not fail on a 115 | * particular field 116 | * @deprecated Superseded by {@link #getFieldFailures()} 117 | */ 118 | @Deprecated 119 | public Object getExpected() { 120 | return _expected; 121 | } 122 | 123 | /** 124 | * Check if comparison failed on any particular fields 125 | * @return true if there are field failures 126 | */ 127 | public boolean isFailureOnField() { 128 | return !_fieldFailures.isEmpty(); 129 | } 130 | 131 | /** 132 | * Check if comparison failed with missing on any particular fields 133 | * @return true if an expected field is missing 134 | */ 135 | public boolean isMissingOnField() { 136 | return !_fieldMissing.isEmpty(); 137 | } 138 | 139 | /** 140 | * Check if comparison failed with unexpected on any particular fields 141 | * @return true if an unexpected field is in the result 142 | */ 143 | public boolean isUnexpectedOnField() { 144 | return !_fieldUnexpected.isEmpty(); 145 | } 146 | 147 | /** 148 | * Dot-separated path the the field that failed comparison 149 | * 150 | * @return a {@code String} instance, or {@code null} if the comparison did 151 | * not fail on a particular field 152 | * @deprecated Superseded by {@link #getFieldFailures()} 153 | */ 154 | @Deprecated 155 | public String getField() { 156 | return _field; 157 | } 158 | 159 | public void fail(String message) { 160 | _success = false; 161 | if (_message.length() == 0) { 162 | _message.append(message); 163 | } else { 164 | _message.append(" ; ").append(message); 165 | } 166 | } 167 | 168 | /** 169 | * Identify that the comparison failed 170 | * @param field Which field failed 171 | * @param expected Expected result 172 | * @param actual Actual result 173 | * @return result of comparision 174 | */ 175 | public JSONCompareResult fail(String field, Object expected, Object actual) { 176 | _fieldFailures.add(new FieldComparisonFailure(field, expected, actual)); 177 | this._field = field; 178 | this._expected = expected; 179 | this._actual = actual; 180 | fail(formatFailureMessage(field, expected, actual)); 181 | return this; 182 | } 183 | 184 | /** 185 | * Identify that the comparison failed 186 | * @param field Which field failed 187 | * @param exception exception containing details of match failure 188 | * @return result of comparision 189 | */ 190 | public JSONCompareResult fail(String field, ValueMatcherException exception) { 191 | fail(field + ": " + exception.getMessage(), exception.getExpected(), exception.getActual()); 192 | return this; 193 | } 194 | 195 | private String formatFailureMessage(String field, Object expected, Object actual) { 196 | return field 197 | + "\nExpected: " 198 | + describe(expected) 199 | + "\n got: " 200 | + describe(actual) 201 | + "\n"; 202 | } 203 | 204 | /** 205 | * Identify the missing field 206 | * @param field missing field 207 | * @param expected expected result 208 | * @return result of comparison 209 | */ 210 | public JSONCompareResult missing(String field, Object expected) { 211 | _fieldMissing.add(new FieldComparisonFailure(field, expected, null)); 212 | fail(formatMissing(field, expected)); 213 | return this; 214 | } 215 | 216 | private String formatMissing(String field, Object expected) { 217 | return field 218 | + "\nExpected: " 219 | + describe(expected) 220 | + "\n but none found\n"; 221 | } 222 | 223 | /** 224 | * Identify unexpected field 225 | * @param field unexpected field 226 | * @param actual actual result 227 | * @return result of comparison 228 | */ 229 | public JSONCompareResult unexpected(String field, Object actual) { 230 | _fieldUnexpected.add(new FieldComparisonFailure(field, null, actual)); 231 | fail(formatUnexpected(field, actual)); 232 | return this; 233 | } 234 | 235 | private String formatUnexpected(String field, Object actual) { 236 | return field 237 | + "\nUnexpected: " 238 | + describe(actual) 239 | + "\n"; 240 | } 241 | 242 | private static String describe(Object value) { 243 | if (value instanceof JSONArray) { 244 | return "a JSON array"; 245 | } else if (value instanceof JSONObject) { 246 | return "a JSON object"; 247 | } else { 248 | return String.valueOf(value); 249 | } 250 | } 251 | 252 | @Override 253 | public String toString() { 254 | return _message.toString(); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/JSONParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import org.json.JSONArray; 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | import org.json.JSONString; 21 | 22 | /** 23 | * Simple JSON parsing utility. 24 | */ 25 | public class JSONParser { 26 | // regular expression to match a number in JSON format. see http://www.json.org/fatfree.html. 27 | // "A number can be represented as integer, real, or floating point. JSON does not support octal or hex 28 | // ... [or] NaN or Infinity". 29 | private static final String NUMBER_REGEX = "-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?"; 30 | 31 | private JSONParser() {} 32 | 33 | /** 34 | * Takes a JSON string and returns either a {@link org.json.JSONObject} or {@link org.json.JSONArray}, 35 | * depending on whether the string represents an object or an array. 36 | * 37 | * @param s Raw JSON string to be parsed 38 | * @return JSONObject or JSONArray 39 | */ 40 | public static Object parseJSON(final String s) { 41 | if (s.trim().startsWith("{")) { 42 | return new JSONObject(s); 43 | } 44 | else if (s.trim().startsWith("[")) { 45 | return new JSONArray(s); 46 | } else if (s.trim().startsWith("\"") 47 | || s.trim().matches(NUMBER_REGEX)) { 48 | return new JSONString() { 49 | @Override 50 | public String toJSONString() { 51 | return s; 52 | } 53 | }; 54 | } 55 | throw new JSONException("Unparsable JSON string: " + s); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/LocationAwareValueMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | /** 16 | * 17 | */ 18 | package org.skyscreamer.jsonassert; 19 | 20 | /** 21 | * A ValueMatcher extension that provides location in form of prefix to the equals method. 22 | * 23 | * @author Duncan Mackinder 24 | * @param Generic Type 25 | */ 26 | public interface LocationAwareValueMatcher extends ValueMatcher { 27 | 28 | /** 29 | * Match actual value with expected value. If match fails any of the 30 | * following may occur, return false, pass failure details to specified 31 | * JSONCompareResult and return true, or throw ValueMatcherException 32 | * containing failure details. Passing failure details to JSONCompareResult 33 | * or returning via ValueMatcherException enables more useful failure 34 | * description for cases where expected value depends entirely or in part on 35 | * configuration of the ValueMatcher and therefore expected value passed to 36 | * this method will not give a useful indication of expected value. 37 | * 38 | * @param prefix 39 | * JSON path of the JSON item being tested 40 | * @param actual 41 | * JSON value being tested 42 | * @param expected 43 | * expected JSON value 44 | * @param result 45 | * JSONCompareResult to which match failure may be passed 46 | * @return true if expected and actual equal or any difference has already 47 | * been passed to specified result instance, false otherwise. 48 | * @throws ValueMatcherException 49 | * if expected and actual values not equal and ValueMatcher 50 | * needs to override default comparison failure message that 51 | * would be generated if this method returned false. 52 | */ 53 | boolean equal(String prefix, T actual, T expected, JSONCompareResult result) throws ValueMatcherException; 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/RegularExpressionValueMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import java.util.regex.Pattern; 18 | import java.util.regex.PatternSyntaxException; 19 | 20 | import org.skyscreamer.jsonassert.ValueMatcher; 21 | 22 | /** 23 | * A JSONassert value matcher that matches actual value to regular expression. 24 | * If non-null regular expression passed to constructor, then all actual values 25 | * will be compared against this constant pattern, ignoring any expected value 26 | * passed to equal method. If null regular expression passed to constructor, 27 | * then expected value passed to equals method will be used to dynamically 28 | * specify regular expression pattern that actual value must match. 29 | * 30 | * @author Duncan Mackinder 31 | * @param Generic Type 32 | */ 33 | public class RegularExpressionValueMatcher implements ValueMatcher { 34 | 35 | private final Pattern expectedPattern; 36 | 37 | /** 38 | * Create RegularExpressionValueMatcher in which the pattern the actual 39 | * value must match with be specified dynamically from the expected string 40 | * passed to this matcher in the equals method. 41 | */ 42 | public RegularExpressionValueMatcher() { 43 | this(null); 44 | } 45 | 46 | /** 47 | * Create RegularExpressionValueMatcher with specified pattern. If pattern 48 | * is not null, it must be a valid regular expression that defines a 49 | * constant expected pattern that every actual value must match (in this 50 | * case the expected value passed to equal method will be ignored). If 51 | * pattern is null, the pattern the actual value must match with be 52 | * specified dynamically from the expected string passed to this matcher in 53 | * the equals method. 54 | * 55 | * @param pattern 56 | * if non null, regular expression pattern which all actual 57 | * values this matcher is applied to must match. If null, this 58 | * matcher will apply pattern specified dynamically via the 59 | * expected parameter to the equal method. 60 | * @throws IllegalArgumentException 61 | * if pattern is non-null and not a valid regular expression. 62 | */ 63 | public RegularExpressionValueMatcher(String pattern) throws IllegalArgumentException { 64 | try { 65 | expectedPattern = pattern == null ? null : Pattern.compile(pattern); 66 | } 67 | catch (PatternSyntaxException e) { 68 | throw new IllegalArgumentException("Constant expected pattern invalid: " + e.getMessage(), e); 69 | } 70 | } 71 | 72 | @Override 73 | public boolean equal(T actual, T expected) { 74 | String actualString = actual.toString(); 75 | String expectedString = expected.toString(); 76 | try { 77 | Pattern pattern = isStaticPattern() ? expectedPattern : Pattern 78 | .compile(expectedString); 79 | if (!pattern.matcher(actualString).matches()) { 80 | throw new ValueMatcherException(getPatternType() + " expected pattern did not match value", pattern.toString(), actualString); 81 | } 82 | } 83 | catch (PatternSyntaxException e) { 84 | throw new ValueMatcherException(getPatternType() + " expected pattern invalid: " + e.getMessage(), e, expectedString, actualString); 85 | } 86 | return true; 87 | } 88 | 89 | private boolean isStaticPattern() { 90 | return expectedPattern != null; 91 | } 92 | 93 | private String getPatternType() { 94 | return isStaticPattern()? "Constant": "Dynamic"; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/ValueMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | /** 18 | * Represents a value matcher that can compare two objects for equality. 19 | * 20 | * @param the object type to compare 21 | */ 22 | public interface ValueMatcher { 23 | 24 | /** 25 | * Compares the two provided objects whether they are equal. 26 | * 27 | * @param o1 the first object to check 28 | * @param o2 the object to check the first against 29 | * @return true if the objects are equal, false otherwise 30 | */ 31 | boolean equal(T o1, T o2); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/ValueMatcherException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | /** 18 | * Exception that may be thrown by ValueMatcher subclasses to provide more detail on why matches method failed. 19 | * 20 | * @author Duncan Mackinder 21 | * 22 | */ 23 | public class ValueMatcherException extends RuntimeException { 24 | private static final long serialVersionUID = 1L; 25 | 26 | private final String expected; 27 | 28 | private final String actual; 29 | 30 | /** 31 | * Create new ValueMatcherException 32 | * 33 | * @param message 34 | * description of exception 35 | * @param expected 36 | * value expected by ValueMatcher 37 | * @param actual 38 | * value being tested by ValueMatcher 39 | */ 40 | public ValueMatcherException(String message, String expected, String actual) { 41 | super(message); 42 | this.expected = expected; 43 | this.actual = actual; 44 | } 45 | 46 | /** 47 | * Create new ValueMatcherException 48 | * 49 | * @param message 50 | * description of exception 51 | * @param cause 52 | * cause of ValueMatcherException 53 | * @param expected 54 | * value expected by ValueMatcher 55 | * @param actual 56 | * value being tested by ValueMatcher 57 | */ 58 | public ValueMatcherException(String message, Throwable cause, String expected, String actual) { 59 | super(message, cause); 60 | this.expected = expected; 61 | this.actual = actual; 62 | } 63 | 64 | /** 65 | * @return the expected value 66 | */ 67 | public String getExpected() { 68 | return expected; 69 | } 70 | 71 | /** 72 | * @return the actual value 73 | */ 74 | public String getActual() { 75 | return actual; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/comparator/AbstractComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert.comparator; 16 | 17 | import org.json.JSONArray; 18 | import org.json.JSONObject; 19 | import org.skyscreamer.jsonassert.JSONCompareResult; 20 | 21 | import java.util.*; 22 | 23 | import static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.*; 24 | 25 | /** 26 | * This class provides a skeletal implementation of the {@link JSONComparator} 27 | * interface, to minimize the effort required to implement this interface. 28 | * 29 | * 30 | */ 31 | public abstract class AbstractComparator implements JSONComparator { 32 | 33 | /** 34 | * Default constructor 35 | */ 36 | public AbstractComparator() { 37 | } 38 | 39 | /** 40 | * Compares JSONObject provided to the expected JSONObject, and returns the results of the comparison. 41 | * 42 | * @param expected Expected JSONObject 43 | * @param actual JSONObject to compare 44 | */ 45 | @Override 46 | public final JSONCompareResult compareJSON(JSONObject expected, JSONObject actual) { 47 | JSONCompareResult result = new JSONCompareResult(); 48 | compareJSON("", expected, actual, result); 49 | return result; 50 | } 51 | 52 | /** 53 | * Compares JSONArray provided to the expected JSONArray, and returns the results of the comparison. 54 | * 55 | * @param expected Expected JSONArray 56 | * @param actual JSONArray to compare 57 | */ 58 | @Override 59 | public final JSONCompareResult compareJSON(JSONArray expected, JSONArray actual) { 60 | JSONCompareResult result = new JSONCompareResult(); 61 | compareJSONArray("", expected, actual, result); 62 | return result; 63 | } 64 | 65 | /** 66 | * @param prefix 67 | * @param expected 68 | * @param actual 69 | * @param result 70 | */ 71 | protected void checkJsonObjectKeysActualInExpected(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) { 72 | Set actualKeys = getKeys(actual); 73 | for (String key : actualKeys) { 74 | if (!expected.has(key)) { 75 | result.unexpected(prefix, key); 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * 82 | * @param prefix 83 | * @param expected 84 | * @param actual 85 | * @param result 86 | */ 87 | protected void checkJsonObjectKeysExpectedInActual(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) { 88 | Set expectedKeys = getKeys(expected); 89 | for (String key : expectedKeys) { 90 | Object expectedValue = expected.get(key); 91 | if (actual.has(key)) { 92 | Object actualValue = actual.get(key); 93 | compareValues(qualify(prefix, key), expectedValue, actualValue, result); 94 | } else { 95 | result.missing(prefix, key); 96 | } 97 | } 98 | } 99 | 100 | protected void compareJSONArrayOfJsonObjects(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) { 101 | String uniqueKey = findUniqueKey(expected); 102 | if (uniqueKey == null || !isUsableAsUniqueKey(uniqueKey, actual)) { 103 | // An expensive last resort 104 | recursivelyCompareJSONArray(key, expected, actual, result); 105 | return; 106 | } 107 | Map expectedValueMap = arrayOfJsonObjectToMap(expected, uniqueKey); 108 | Map actualValueMap = arrayOfJsonObjectToMap(actual, uniqueKey); 109 | for (Object id : expectedValueMap.keySet()) { 110 | if (!actualValueMap.containsKey(id)) { 111 | result.missing(formatUniqueKey(key, uniqueKey, id), expectedValueMap.get(id)); 112 | continue; 113 | } 114 | JSONObject expectedValue = expectedValueMap.get(id); 115 | JSONObject actualValue = actualValueMap.get(id); 116 | compareValues(formatUniqueKey(key, uniqueKey, id), expectedValue, actualValue, result); 117 | } 118 | for (Object id : actualValueMap.keySet()) { 119 | if (!expectedValueMap.containsKey(id)) { 120 | result.unexpected(formatUniqueKey(key, uniqueKey, id), actualValueMap.get(id)); 121 | } 122 | } 123 | } 124 | 125 | protected void compareJSONArrayOfSimpleValues(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) { 126 | Map expectedCount = JSONCompareUtil.getCardinalityMap(jsonArrayToList(expected)); 127 | Map actualCount = JSONCompareUtil.getCardinalityMap(jsonArrayToList(actual)); 128 | for (Object o : expectedCount.keySet()) { 129 | if (!actualCount.containsKey(o)) { 130 | result.missing(key + "[]", o); 131 | } else if (!actualCount.get(o).equals(expectedCount.get(o))) { 132 | result.fail(key + "[]: Expected " + expectedCount.get(o) + " occurrence(s) of " + o 133 | + " but got " + actualCount.get(o) + " occurrence(s)"); 134 | } 135 | } 136 | for (Object o : actualCount.keySet()) { 137 | if (!expectedCount.containsKey(o)) { 138 | result.unexpected(key + "[]", o); 139 | } 140 | } 141 | } 142 | 143 | protected void compareJSONArrayWithStrictOrder(String key, JSONArray expected, JSONArray actual, JSONCompareResult result) { 144 | for (int i = 0; i < expected.length(); ++i) { 145 | Object expectedValue = JSONCompareUtil.getObjectOrNull(expected, i); 146 | Object actualValue = JSONCompareUtil.getObjectOrNull(actual, i); 147 | compareValues(key + "[" + i + "]", expectedValue, actualValue, result); 148 | } 149 | } 150 | 151 | // This is expensive (O(n^2) -- yuck), but may be the only resort for some cases with loose array ordering, and no 152 | // easy way to uniquely identify each element. 153 | // This is expensive (O(n^2) -- yuck), but may be the only resort for some cases with loose array ordering, and no 154 | // easy way to uniquely identify each element. 155 | protected void recursivelyCompareJSONArray(String key, JSONArray expected, JSONArray actual, 156 | JSONCompareResult result) { 157 | Set matched = new HashSet(); 158 | for (int i = 0; i < expected.length(); ++i) { 159 | Object expectedElement = JSONCompareUtil.getObjectOrNull(expected, i); 160 | boolean matchFound = false; 161 | for (int j = 0; j < actual.length(); ++j) { 162 | Object actualElement = JSONCompareUtil.getObjectOrNull(actual, j); 163 | if (expectedElement == actualElement) { 164 | matchFound = true; 165 | break; 166 | } 167 | if ((expectedElement == null && actualElement != null) || (expectedElement != null && actualElement == null)) { 168 | continue; 169 | } 170 | if (matched.contains(j) || !actualElement.getClass().equals(expectedElement.getClass())) { 171 | continue; 172 | } 173 | if (expectedElement instanceof JSONObject) { 174 | if (compareJSON((JSONObject) expectedElement, (JSONObject) actualElement).passed()) { 175 | matched.add(j); 176 | matchFound = true; 177 | break; 178 | } 179 | } else if (expectedElement instanceof JSONArray) { 180 | if (compareJSON((JSONArray) expectedElement, (JSONArray) actualElement).passed()) { 181 | matched.add(j); 182 | matchFound = true; 183 | break; 184 | } 185 | } else if (expectedElement.equals(actualElement)) { 186 | matched.add(j); 187 | matchFound = true; 188 | break; 189 | } 190 | } 191 | if (!matchFound) { 192 | result.fail(key + "[" + i + "] Could not find match for element " + expectedElement); 193 | return; 194 | } 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/comparator/ArraySizeComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert.comparator; 16 | 17 | import java.text.MessageFormat; 18 | 19 | import org.json.JSONArray; 20 | import org.skyscreamer.jsonassert.JSONCompareMode; 21 | import org.skyscreamer.jsonassert.JSONCompareResult; 22 | 23 | /** 24 | * A JSONAssert array size comparator. 25 | * 26 | *

Some typical usage idioms are listed below.

27 | * 28 | *

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

29 | * 30 | * {a:[7, 8, 9]} 31 | * 32 | *

then:

33 | * 34 | *

To verify that array 'a' contains 3 elements:

35 | * 36 | * 37 | * JSONAssert.assertEquals("{a:[3]}", ARRAY_OF_JSONOBJECTS, new ArraySizeComparator(JSONCompareMode.LENIENT)); 38 | * 39 | * 40 | *

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

41 | * 42 | * 43 | * JSONAssert.assertEquals("{a:[2,6]}", ARRAY_OF_JSONOBJECTS, new ArraySizeComparator(JSONCompareMode.LENIENT)); 44 | * 45 | * 46 | * @author Duncan Mackinder 47 | * 48 | */ 49 | public class ArraySizeComparator extends DefaultComparator { 50 | 51 | /** 52 | * Create new ArraySizeComparator. 53 | * 54 | * @param mode 55 | * comparison mode, has no impact on ArraySizeComparator but is 56 | * used by instance of superclass DefaultComparator to control 57 | * comparison of JSON items other than arrays. 58 | */ 59 | public ArraySizeComparator(JSONCompareMode mode) { 60 | super(mode); 61 | } 62 | 63 | /** 64 | * Expected array should consist of either 1 or 2 integer values that define 65 | * maximum and minimum valid lengths of the actual array. If expected array 66 | * contains a single integer value, then the actual array must contain 67 | * exactly that number of elements. 68 | */ 69 | @Override 70 | public void compareJSONArray(String prefix, JSONArray expected, 71 | JSONArray actual, JSONCompareResult result) { 72 | String arrayPrefix = prefix + "[]"; 73 | if (expected.length() < 1 || expected.length() > 2) { 74 | result.fail(MessageFormat 75 | .format("{0}: invalid expectation: expected array should contain either 1 or 2 elements but contains {1} elements", 76 | arrayPrefix, expected.length())); 77 | return; 78 | } 79 | if (!(expected.get(0) instanceof Number)) { 80 | result.fail(MessageFormat 81 | .format("{0}: invalid expectation: {1}expected array size ''{2}'' not a number", 82 | arrayPrefix, (expected.length() == 1? "": "minimum "), expected.get(0))); 83 | return; 84 | } 85 | if ((expected.length() == 2 && !(expected.get(1) instanceof Number))) { 86 | result.fail(MessageFormat 87 | .format("{0}: invalid expectation: maximum expected array size ''{1}'' not a number", 88 | arrayPrefix, expected.get(1))); 89 | return; 90 | } 91 | int minExpectedLength = expected.getInt(0); 92 | if (minExpectedLength < 0) { 93 | result.fail(MessageFormat 94 | .format("{0}: invalid expectation: minimum expected array size ''{1}'' negative", 95 | arrayPrefix, minExpectedLength)); 96 | return; 97 | } 98 | int maxExpectedLength = expected.length() == 2 ? expected.getInt(1) 99 | : minExpectedLength; 100 | if (maxExpectedLength < minExpectedLength) { 101 | result.fail(MessageFormat 102 | .format("{0}: invalid expectation: maximum expected array size ''{1}'' less than minimum expected array size ''{2}''", 103 | arrayPrefix, maxExpectedLength, minExpectedLength)); 104 | return; 105 | } 106 | if (actual.length() < minExpectedLength 107 | || actual.length() > maxExpectedLength) { 108 | result.fail( 109 | arrayPrefix, 110 | MessageFormat.format( 111 | "array size of {0}{1} elements", 112 | minExpectedLength, 113 | (expected.length() == 2 ? (" to " + maxExpectedLength) 114 | : "")), 115 | MessageFormat.format("{0} elements", 116 | actual.length())); 117 | } 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/comparator/CustomComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert.comparator; 16 | 17 | import org.skyscreamer.jsonassert.Customization; 18 | import org.skyscreamer.jsonassert.JSONCompareMode; 19 | import org.skyscreamer.jsonassert.JSONCompareResult; 20 | import org.skyscreamer.jsonassert.ValueMatcherException; 21 | 22 | import java.util.Arrays; 23 | import java.util.Collection; 24 | 25 | public class CustomComparator extends DefaultComparator { 26 | 27 | private final Collection customizations; 28 | 29 | public CustomComparator(JSONCompareMode mode, Customization... customizations) { 30 | super(mode); 31 | this.customizations = Arrays.asList(customizations); 32 | } 33 | 34 | @Override 35 | public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) { 36 | Customization customization = getCustomization(prefix); 37 | if (customization != null) { 38 | try { 39 | if (!customization.matches(prefix, actualValue, expectedValue, result)) { 40 | result.fail(prefix, expectedValue, actualValue); 41 | } 42 | } 43 | catch (ValueMatcherException e) { 44 | result.fail(prefix, e); 45 | } 46 | } else { 47 | super.compareValues(prefix, expectedValue, actualValue, result); 48 | } 49 | } 50 | 51 | private Customization getCustomization(String path) { 52 | for (Customization c : customizations) 53 | if (c.appliesToPath(path)) 54 | return c; 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/comparator/DefaultComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert.comparator; 16 | 17 | import org.json.JSONArray; 18 | import org.json.JSONObject; 19 | import org.skyscreamer.jsonassert.JSONCompareMode; 20 | import org.skyscreamer.jsonassert.JSONCompareResult; 21 | 22 | import static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.allJSONObjects; 23 | import static org.skyscreamer.jsonassert.comparator.JSONCompareUtil.allSimpleValues; 24 | 25 | /** 26 | * This class is the default json comparator implementation. 27 | * Comparison is performed according to {@link JSONCompareMode} that is passed as constructor's argument. 28 | */ 29 | public class DefaultComparator extends AbstractComparator { 30 | 31 | JSONCompareMode mode; 32 | 33 | public DefaultComparator(JSONCompareMode mode) { 34 | this.mode = mode; 35 | } 36 | 37 | @Override 38 | public void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) { 39 | // Check that actual contains all the expected values 40 | checkJsonObjectKeysExpectedInActual(prefix, expected, actual, result); 41 | 42 | // If strict, check for vice-versa 43 | if (!mode.isExtensible()) { 44 | checkJsonObjectKeysActualInExpected(prefix, expected, actual, result); 45 | } 46 | } 47 | 48 | @Override 49 | public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) { 50 | if (expectedValue == actualValue) { 51 | return; 52 | } 53 | if (expectedValue == null || actualValue == null) { 54 | result.fail(prefix, expectedValue, actualValue); 55 | } else if (areNumbers(expectedValue, actualValue)) { 56 | if (areNotSameDoubles(expectedValue, actualValue)) { 57 | result.fail(prefix, expectedValue, actualValue); 58 | } 59 | } else if (expectedValue.getClass().isAssignableFrom(actualValue.getClass())) { 60 | if (expectedValue instanceof JSONArray) { 61 | compareJSONArray(prefix, (JSONArray) expectedValue, (JSONArray) actualValue, result); 62 | } else if (expectedValue instanceof JSONObject) { 63 | compareJSON(prefix, (JSONObject) expectedValue, (JSONObject) actualValue, result); 64 | } else if (!expectedValue.equals(actualValue)) { 65 | result.fail(prefix, expectedValue, actualValue); 66 | } 67 | } else { 68 | result.fail(prefix, expectedValue, actualValue); 69 | } 70 | } 71 | 72 | @Override 73 | public void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) { 74 | if (expected.length() != actual.length()) { 75 | result.fail(prefix + "[]: Expected " + expected.length() + " values but got " + actual.length()); 76 | return; 77 | } else if (expected.length() == 0) { 78 | return; // Nothing to compare 79 | } 80 | 81 | if (mode.hasStrictOrder()) { 82 | compareJSONArrayWithStrictOrder(prefix, expected, actual, result); 83 | } else if (allSimpleValues(expected)) { 84 | compareJSONArrayOfSimpleValues(prefix, expected, actual, result); 85 | } else if (allJSONObjects(expected)) { 86 | compareJSONArrayOfJsonObjects(prefix, expected, actual, result); 87 | } else { 88 | // An expensive last resort 89 | recursivelyCompareJSONArray(prefix, expected, actual, result); 90 | } 91 | } 92 | 93 | protected boolean areNumbers(Object expectedValue, Object actualValue) { 94 | return expectedValue instanceof Number && actualValue instanceof Number; 95 | } 96 | 97 | protected boolean areNotSameDoubles(Object expectedValue, Object actualValue) { 98 | return ((Number) expectedValue).doubleValue() != ((Number) actualValue).doubleValue(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/comparator/JSONComparator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert.comparator; 16 | 17 | import org.json.JSONArray; 18 | import org.json.JSONObject; 19 | import org.skyscreamer.jsonassert.JSONCompareResult; 20 | 21 | /** 22 | * Interface for comparison handler. 23 | * 24 | * @author Ivan Zaytsev 25 | * 2013-01-04 26 | */ 27 | public interface JSONComparator { 28 | 29 | /** 30 | * Compares two {@link JSONObject}s and returns the result of the comparison in a {@link JSONCompareResult} object. 31 | * 32 | * @param expected the expected JSON object 33 | * @param actual the actual JSON object 34 | * @return the result of the comparison 35 | */ 36 | JSONCompareResult compareJSON(JSONObject expected, JSONObject actual); 37 | 38 | /** 39 | * Compares two {@link JSONArray}s and returns the result of the comparison in a {@link JSONCompareResult} object. 40 | * 41 | * @param expected the expected JSON array 42 | * @param actual the actual JSON array 43 | * @return the result of the comparison 44 | */ 45 | JSONCompareResult compareJSON(JSONArray expected, JSONArray actual); 46 | 47 | /** 48 | * Compares two {@link JSONObject}s on the provided path represented by {@code prefix} and 49 | * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object. 50 | * 51 | * @param prefix the path in the json where the comparison happens 52 | * @param expected the expected JSON object 53 | * @param actual the actual JSON object 54 | * @param result stores the actual state of the comparison result 55 | */ 56 | void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result); 57 | 58 | /** 59 | * Compares two {@link Object}s on the provided path represented by {@code prefix} and 60 | * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object. 61 | * 62 | * @param prefix the path in the json where the comparison happens 63 | * @param expectedValue the expected value 64 | * @param actualValue the actual value 65 | * @param result stores the actual state of the comparison result 66 | */ 67 | void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result); 68 | 69 | /** 70 | * Compares two {@link JSONArray}s on the provided path represented by {@code prefix} and 71 | * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object. 72 | * 73 | * @param prefix the path in the json where the comparison happens 74 | * @param expected the expected JSON array 75 | * @param actual the actual JSON array 76 | * @param result stores the actual state of the comparison result 77 | */ 78 | void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result); 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert.comparator; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Collection; 19 | import java.util.HashMap; 20 | import java.util.HashSet; 21 | import java.util.Iterator; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Set; 25 | import java.util.TreeSet; 26 | 27 | import org.json.JSONArray; 28 | import org.json.JSONObject; 29 | 30 | /** 31 | * Utility class that contains Json manipulation methods. 32 | */ 33 | public final class JSONCompareUtil { 34 | private static Integer INTEGER_ONE = new Integer(1); 35 | 36 | private JSONCompareUtil() { 37 | } 38 | 39 | /** 40 | * Converts the provided {@link JSONArray} to a Map of {@link JSONObject}s where the key of each object 41 | * is the value at {@code uniqueKey} in each object. 42 | * 43 | * @param array the JSON array to convert 44 | * @param uniqueKey the key to map the JSON objects to 45 | * @return the map of {@link JSONObject}s from {@code array} 46 | */ 47 | public static Map arrayOfJsonObjectToMap(JSONArray array, String uniqueKey) { 48 | Map valueMap = new HashMap(); 49 | for (int i = 0; i < array.length(); ++i) { 50 | JSONObject jsonObject = (JSONObject) array.get(i); 51 | Object id = jsonObject.get(uniqueKey); 52 | valueMap.put(id, jsonObject); 53 | } 54 | return valueMap; 55 | } 56 | 57 | /** 58 | * Searches for the unique key of the {@code expected} JSON array. 59 | * 60 | * @param expected the array to find the unique key of 61 | * @return the unique key if there's any, otherwise null 62 | */ 63 | public static String findUniqueKey(JSONArray expected) { 64 | // Find a unique key for the object (id, name, whatever) 65 | JSONObject o = (JSONObject) expected.get(0); // There's at least one at this point 66 | for (String candidate : getKeys(o)) { 67 | if (isUsableAsUniqueKey(candidate, expected)) return candidate; 68 | } 69 | // No usable unique key :-( 70 | return null; 71 | } 72 | 73 | /** 74 | *

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

76 | *
    77 | *
  1. array is an array of JSONObject 78 | *
  2. candidate is a top-level field in each of of the objects in the array 79 | *
  3. candidate is a simple value (not JSONObject or JSONArray) 80 | *
  4. candidate is unique across all elements in the array 81 | *
82 | * 83 | * @param candidate is usable as a unique key if every element in the 84 | * @param array is a JSONObject having that key, and no two values are the same. 85 | * @return true if the candidate can work as a unique id across array 86 | */ 87 | public static boolean isUsableAsUniqueKey(String candidate, JSONArray array) { 88 | Set seenValues = new HashSet(); 89 | for (int i = 0; i < array.length(); i++) { 90 | Object item = array.get(i); 91 | if (item instanceof JSONObject) { 92 | JSONObject o = (JSONObject) item; 93 | if (o.has(candidate)) { 94 | Object value = o.get(candidate); 95 | if (isSimpleValue(value) && !seenValues.contains(value)) { 96 | seenValues.add(value); 97 | } else { 98 | return false; 99 | } 100 | } else { 101 | return false; 102 | } 103 | } else { 104 | return false; 105 | } 106 | } 107 | return true; 108 | } 109 | 110 | /** 111 | * Converts the given {@link JSONArray} to a list of {@link Object}s. 112 | * 113 | * @param expected the JSON array to convert 114 | * @return the list of objects from the {@code expected} array 115 | */ 116 | public static List jsonArrayToList(JSONArray expected) { 117 | List jsonObjects = new ArrayList(expected.length()); 118 | for (int i = 0; i < expected.length(); ++i) { 119 | jsonObjects.add(getObjectOrNull(expected, i)); 120 | } 121 | return jsonObjects; 122 | } 123 | 124 | /** 125 | * Returns the value present in the given index position. If null value is present, it will return null 126 | * 127 | * @param jsonArray the JSON array to get value from 128 | * @param index index of object to retrieve 129 | * @return value at the given index position 130 | */ 131 | public static Object getObjectOrNull(JSONArray jsonArray, int index) { 132 | return jsonArray.isNull(index) ? null : jsonArray.get(index); 133 | } 134 | 135 | /** 136 | * Returns whether all of the elements in the given array are simple values. 137 | * 138 | * @param array the JSON array to iterate through on 139 | * @return true if all the elements in {@code array} are simple values 140 | * @see #isSimpleValue(Object) 141 | */ 142 | public static boolean allSimpleValues(JSONArray array) { 143 | for (int i = 0; i < array.length(); ++i) { 144 | if (!array.isNull(i) && !isSimpleValue(array.get(i))) { 145 | return false; 146 | } 147 | } 148 | return true; 149 | } 150 | 151 | /** 152 | * Returns whether the given object is a simple value: not {@link JSONObject} and not {@link JSONArray}. 153 | * 154 | * @param o the object to inspect 155 | * @return true if {@code o} is a simple value 156 | */ 157 | public static boolean isSimpleValue(Object o) { 158 | return !(o instanceof JSONObject) && !(o instanceof JSONArray); 159 | } 160 | 161 | /** 162 | * Returns whether all elements in {@code array} are {@link JSONObject} instances. 163 | * 164 | * @param array the array to inspect 165 | * @return true if all the elements in the given array are JSONObjects 166 | */ 167 | public static boolean allJSONObjects(JSONArray array) { 168 | for (int i = 0; i < array.length(); ++i) { 169 | if (!(array.get(i) instanceof JSONObject)) { 170 | return false; 171 | } 172 | } 173 | return true; 174 | } 175 | 176 | /** 177 | * Returns whether all elements in {@code array} are {@link JSONArray} instances. 178 | * 179 | * @param array the array to inspect 180 | * @return true if all the elements in the given array are JSONArrays 181 | */ 182 | public static boolean allJSONArrays(JSONArray array) { 183 | for (int i = 0; i < array.length(); ++i) { 184 | if (!(array.get(i) instanceof JSONArray)) { 185 | return false; 186 | } 187 | } 188 | return true; 189 | } 190 | 191 | /** 192 | * Collects all keys in {@code jsonObject}. 193 | * 194 | * @param jsonObject the {@link JSONObject} to get the keys of 195 | * @return the set of keys 196 | */ 197 | public static Set getKeys(JSONObject jsonObject) { 198 | Set keys = new TreeSet(); 199 | Iterator iter = jsonObject.keys(); 200 | while (iter.hasNext()) { 201 | keys.add((String) iter.next()); 202 | } 203 | return keys; 204 | } 205 | 206 | public static String qualify(String prefix, String key) { 207 | return "".equals(prefix) ? key : prefix + "." + key; 208 | } 209 | 210 | public static String formatUniqueKey(String key, String uniqueKey, Object value) { 211 | return key + "[" + uniqueKey + "=" + value + "]"; 212 | } 213 | 214 | /** 215 | * Creates a cardinality map from {@code coll}. 216 | * 217 | * @param coll the collection of items to convert 218 | * @param the type of elements in the input collection 219 | * @return the cardinality map 220 | */ 221 | public static Map getCardinalityMap(final Collection coll) { 222 | Map count = new HashMap(); 223 | for (T item : coll) { 224 | Integer c = (Integer) (count.get(item)); 225 | if (c == null) { 226 | count.put(item, INTEGER_ONE); 227 | } else { 228 | count.put(item, new Integer(c.intValue() + 1)); 229 | } 230 | } 231 | return count; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/site/resources/CNAME: -------------------------------------------------------------------------------- 1 | jsonassert.skyscreamer.org 2 | 3 | -------------------------------------------------------------------------------- /src/site/resources/cookbook.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JSONAssert - Write JSON Unit Tests with Less Code - Cookbook 5 | 6 | 7 | 8 | 9 | 23 | 24 | 25 |
26 |

JSONassert

27 |

a Skyscreamer project

28 |
29 |
30 | 40 |
41 | 42 |

Cookbook

43 | 44 |

Assertion parameters can be a java.lang.String with JSON data, an org.json.JSONObject, or an org.json.JSONArray. 45 | For readability, we'll use strings in the following examples.

46 | 47 |
48 |

A really simple example. Get a JSON object and test its ID:

49 |
50 | @Test
51 | public void testSimple() {
52 |   String result = getJSONResult("/user/1.json");
53 |   JSONAssert.assertEquals("{id:1}", result, false); // Pass
54 | }
55 |
56 |
57 | 58 | 66 | 67 |
68 |

Strict or not, field order does not matter:

69 |
70 | String result = "{id:1,name:\"Juergen\"}";
71 | JSONAssert.assertEquals("{name:\"Juergen\",id:1}", result, true); // Pass
72 |
73 |
74 | 75 | 76 |

Because application interfaces are naturally extended as they mature, it is recommended that you 77 | default to leaving strict mode off, except in particular cases.

78 | 79 |
80 |

Arrays rules are different. If sequence is important, you can enable strict mode:

81 |
82 | String result = "[1,2,3,4,5]";
83 | JSONAssert.assertEquals("[1,2,3,4,5]", result, true); // Pass
84 | JSONAssert.assertEquals("[5,3,2,1,4]", result, true); // Fail
85 |
86 |
87 | 88 |
89 |

When strict mode is off, arrays can be in any order:

90 |
91 | String result = "[1,2,3,4,5]";
92 | JSONAssert.assertEquals("[5,3,2,1,4]", result, false); // Pass
93 |
94 |
95 | 96 | 105 | 106 |

The example so far are simple, but this will work for JSON objects of any size (per VM memory limits), depth, 107 | or complexity.

108 | 109 | 117 | 118 | 126 | 127 | 135 |
136 | 137 | 138 | -------------------------------------------------------------------------------- /src/site/resources/css/style.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0 none; 17 | font-size: 100%; 18 | font: inherit; 19 | vertical-align: baseline; 20 | } 21 | article, aside, details, figcaption, figure, 22 | footer, header, hgroup, menu, nav, section { 23 | display: block; 24 | } 25 | html { 26 | color: #000000; 27 | background: #ffffff; 28 | overflow-y: scroll; 29 | } 30 | body {line-height: 1;} 31 | h1, h2, h3, h4, h5, h6 {font-weight: normal;} 32 | ol, ul {list-style: none;} 33 | blockquote, q {quotes: none;} 34 | blockquote:before, blockquote:after, 35 | q:before, q:after { 36 | content: ''; 37 | content: none; 38 | } 39 | table { 40 | border-collapse: collapse; 41 | border-spacing: 0; 42 | } 43 | caption, th {text-align: left;} 44 | *:focus {outline: none 0;} 45 | input::-moz-focus-inner, a img,:link img,:visited img {border: 0 none;} 46 | :link, :visited, :hover, :active {text-decoration: none;} 47 | address, caption, cite, code, dfn, em, strong, th, var { 48 | font-style: normal; 49 | font-weight: normal; 50 | } 51 | textarea {overflow: auto;} 52 | 53 | 54 | a{ 55 | color: #339CA5; 56 | } 57 | 58 | /* Default Layout: 992px. 59 | Gutters: 24px. 60 | Outer margins: 48px. 61 | Leftover space for scrollbars @1024px: 32px. 62 | ------------------------------------------------------------------------------- 63 | cols 1 2 3 4 5 6 7 8 9 10 64 | px 68 160 252 344 436 528 620 712 804 896 */ 65 | 66 | body { 67 | font-family: "Lucida Grande", Verdana, Tahoma, sans-serif; 68 | font-size: 14px; 69 | line-height: 15px; 70 | color: #444; 71 | padding: 48px 48px 84px; 72 | margin: 0 auto; 73 | width: 896px; 74 | -webkit-text-size-adjust: 100%; /* Stops Mobile Safari from auto-adjusting font-sizes */ 75 | } 76 | nav { 77 | background: #BEBBBB; 78 | letter-spacing: .5px; 79 | overflow: auto; 80 | top: 0; 81 | -webkit-box-shadow: 0 3px 3px 0 #ccf; 82 | -moz-box-shadow: 0 3px 3px 0 #ccf; 83 | box-shadow: 0 2px 6px 0 #bbb; 84 | } 85 | nav a { 86 | display: block; 87 | color: #fff; 88 | float: left; 89 | padding: 10px 18px; 90 | border-right: 1px solid #eee; 91 | } 92 | nav .intro { 93 | background: #3a3a3a; 94 | } 95 | nav .cookbook { 96 | background: #339CA5; 97 | } 98 | nav .quickstart { 99 | background: #757575; 100 | } 101 | nav .javadoc { 102 | background: #757575; 103 | } 104 | nav .download { 105 | background: #929090; 106 | } 107 | nav .contrib { 108 | background: #a6a6a6; 109 | background-image:url('http://skyscreamer.org/i/blacktocat.png'); 110 | background-repeat:no-repeat; 111 | background-position:center; 112 | background-origin:content-box; 113 | } 114 | nav a.active { 115 | background: #f3444e; 116 | } 117 | header { } 118 | header h1 { 119 | clear: both; 120 | color: #339CA5; 121 | font-size: 43px; 122 | line-height: 60px; 123 | float: left; 124 | width: 70%; 125 | } 126 | header h2 { 127 | padding: 33px 0 0 0; 128 | float: left; 129 | text-align: right; 130 | width: 30%; 131 | } 132 | header h2 a { 133 | color: #950510; 134 | } 135 | header ul li { 136 | float: left; 137 | } 138 | section { 139 | clear: both; 140 | margin: 0 0 20px 0; 141 | padding: 0 20px; 142 | } 143 | section h2 { 144 | border-bottom: 2px solid #339CA5; 145 | font-size: 20px; 146 | line-height: 21px; 147 | margin-bottom: 20px; 148 | padding-top: 30px; 149 | } 150 | section p { 151 | margin: 10px 0; 152 | } 153 | section blockquote, 154 | section pre { 155 | background: #B2F1F7; 156 | border: 3px double #aaa; 157 | padding: 10px; 158 | } 159 | section pre { 160 | overflow: auto; 161 | font: 11px "Courier New", Courier, monospace; 162 | } 163 | section blockquote a { 164 | font: 12px "Courier New", Courier, monospace; 165 | color: #000; 166 | } 167 | section .demo { 168 | background: #d1d1ff; 169 | border: 3px double #aaa; 170 | float: left; 171 | margin: 20px 0 20px 0; 172 | padding: 8px; 173 | width: 380px; 174 | } 175 | section .demo h5 { 176 | font-size: 13px; 177 | font-weight: bold; 178 | } 179 | section .demo .wrapper { 180 | display: none; 181 | font: 12px "Courier New", Courier, monospace; 182 | margin: 0; 183 | padding: 0; 184 | } 185 | section .wrapper article h4 { 186 | font-weight: bold; 187 | margin-top: 20px; 188 | } 189 | section .demo .wrapper h4 { 190 | font-size: 15px; 191 | } 192 | section .demo input, 193 | section .demo textarea { 194 | border: 1px solid #888; 195 | margin-top: 30px; 196 | width: 100%; 197 | } 198 | section .demo textarea { 199 | height: 150px; 200 | } 201 | section .demo2 { 202 | float: right; 203 | } 204 | section div.example { 205 | margin: 30px 20px; 206 | } 207 | section div.example p { 208 | font: 12px Verdana; 209 | margin-bottom: 3px; 210 | } 211 | section ul { 212 | list-style: circle; 213 | margin-left: 20px; 214 | } 215 | section ul li { 216 | margin-bottom: 8px; 217 | } 218 | 219 | .strikethrough { text-decoration: line-through; } 220 | 221 | .emphasize { font-weight: bold; } 222 | 223 | .perf_table { 224 | border: #30873E 2px solid; 225 | width: 800px; 226 | } 227 | 228 | .perf_table th { 229 | border: #30873E 1px solid; 230 | padding: 3px; 231 | spacing: 3px; 232 | background-color: #30873E; 233 | color: #FFFFFF; 234 | } 235 | 236 | .perf_table td { 237 | border: #30873E 1px solid; 238 | padding: 3px; 239 | spacing: 3px; 240 | } 241 | 242 | .perf_table .alt_row td { 243 | border: #30873E 1px solid; 244 | padding: 3px; 245 | spacing: 3px; 246 | background-color: #7EC16A; 247 | } 248 | 249 | 250 | 251 | /* Tablet Layout: 768px. 252 | Gutters: 24px. 253 | Outer margins: 28px. 254 | Inherits styles from: Default Layout. 255 | ----------------------------------------------------------------- 256 | cols 1 2 3 4 5 6 7 8 257 | px 68 160 252 344 436 528 620 712 */ 258 | 259 | @media only screen and (min-width: 768px) and (max-width: 991px) { 260 | 261 | body { 262 | width: 712px; 263 | padding: 48px 28px 60px; 264 | } 265 | section .demo { 266 | width: 285px; 267 | } 268 | } 269 | 270 | 271 | 272 | /* Mobile Layout: 320px. 273 | Gutters: 24px. 274 | Outer margins: 34px. 275 | Inherits styles from: Default Layout. 276 | --------------------------------------------- 277 | cols 1 2 3 278 | px 68 160 252 */ 279 | 280 | @media only screen and (max-width: 767px) { 281 | 282 | body { 283 | width: 252px; 284 | padding: 48px 34px 60px; 285 | } 286 | nav li a { 287 | float: none; 288 | display: block; 289 | } 290 | section .demo { 291 | float: left; 292 | width: 200px; 293 | } 294 | 295 | } 296 | 297 | 298 | 299 | /* Wide Mobile Layout: 480px. 300 | Gutters: 24px. 301 | Outer margins: 22px. 302 | Inherits styles from: Default Layout, Mobile Layout. 303 | ------------------------------------------------------------ 304 | cols 1 2 3 4 5 305 | px 68 160 252 344 436 */ 306 | 307 | @media only screen and (min-width: 480px) and (max-width: 767px) { 308 | 309 | body { 310 | width: 436px; 311 | padding: 48px 22px 48px; 312 | } 313 | section .demo { 314 | float: left; 315 | } 316 | 317 | } 318 | -------------------------------------------------------------------------------- /src/site/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JSONAssert - Write JSON Unit Tests with Less Code - Introduction 5 | 6 | 7 | 8 | 9 | 23 | 24 | 25 |
26 |

JSONassert

27 |

a Skyscreamer project

28 |
29 |
30 | 40 |
41 | 42 |

Introduction

43 | 44 |

Write JSON unit tests in less code. Great for testing REST interfaces.

45 | 46 |


Code JSON tests as if you are comparing a string. Under the covers, JSONassert converts your string into a JSON object and compares the logical structure and data with the actual JSON. When strict is set to false (recommended), it forgives reordering data and extending results (as long as all the expected elements are there), making tests less brittle.

47 | 48 |


Supported test frameworks:

49 | 50 | 53 | 54 |


The current version of JSONassert is 2.0-rc1

55 | 56 |

Examples

57 | 58 | 66 | 67 | 97 | 98 |

Error Messages

99 | 100 |
101 |

We tried to make error messages easy to understand. This is really important, since it gets hard for the eye to pick out the difference, particularly in long JSON strings. For example:

102 |
103 | String expected = "{id:1,name:\"Joe\",friends:[{id:2,name:\"Pat\",pets:[\"dog\"]},{id:3,name:\"Sue\",pets:[\"bird\",\"fish\"]}],pets:[]}";
104 | String actual = "{id:1,name:\"Joe\",friends:[{id:2,name:\"Pat\",pets:[\"dog\"]},{id:3,name:\"Sue\",pets:[\"cat\",\"fish\"]}],pets:[]}"
105 | JSONAssert.assertEquals(expected, actual, false);
106 |
107 |
108 | 109 | 115 | 116 | ... which tells you that the pets array under the friend where id=3 was supposed to contain "bird", but had "cat" instead. 117 | (Maybe the cat ate the bird?) 118 |
119 | 120 |
121 | 122 |

Contact

123 |

124 | This is open source so if you want to help out whether by submitting code, design, suggestions, feedback, 125 | or feature requests, we appreciate whatever you can contribute. Contact us at 126 | jsonassert-dev@skyscreamer.org with questions or ideas. 127 |

128 |
129 | 130 | 131 | -------------------------------------------------------------------------------- /src/site/resources/quickstart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | JSONAssert - Write JSON Unit Tests with Less Code - Quickstart 5 | 6 | 7 | 8 | 9 | 23 | 24 | 25 |
26 |

JSONassert

27 |

a Skyscreamer project

28 |
29 |
30 | 40 |
41 | 42 |

Quick Start

43 | 44 |

To use, download and build the JAR or 45 | add the following to your project's pom.xml:

46 | 47 | 56 | 57 |

Syntax is simple, and similar to JUnit Assert:

58 | 63 | 64 |

Add JSONassert tests within existing JUnit tests, just like you would add a standard Assert:

65 | 66 | 76 | 77 |

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

81 |
82 | 83 | 84 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/ArrayValueMatcherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertTrue; 19 | import static org.junit.Assert.fail; 20 | 21 | import java.text.MessageFormat; 22 | 23 | import org.json.JSONArray; 24 | import org.json.JSONObject; 25 | import org.junit.Test; 26 | import org.skyscreamer.jsonassert.comparator.ArraySizeComparator; 27 | import org.skyscreamer.jsonassert.comparator.CustomComparator; 28 | import org.skyscreamer.jsonassert.comparator.DefaultComparator; 29 | import org.skyscreamer.jsonassert.comparator.JSONComparator; 30 | 31 | /** 32 | * Unit tests for ArrayValueMatcher 33 | * 34 | * @author Duncan Mackinder 35 | * 36 | */ 37 | public class ArrayValueMatcherTest { 38 | 39 | 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}]}"; 40 | private static final String ARRAY_OF_INTEGERS = "{a:[1,2,3,4,5]}"; 41 | private static final String ARRAY_OF_JSONARRAYS = "{a:[[6,7,8],[9,10,11],[12,13,14],[19,20,21,22]]}"; 42 | private static final JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); 43 | 44 | private void doTest(String jsonPath, ArrayValueMatcher arrayValueMatcher, String expectedJSON, 45 | String actualJSON) { 46 | Customization customization = new Customization(jsonPath, arrayValueMatcher); 47 | JSONAssert.assertEquals(expectedJSON, actualJSON, new CustomComparator(JSONCompareMode.LENIENT, customization)); 48 | } 49 | 50 | private void doFailingMatchTest(String jsonPath, ArrayValueMatcher arrayValueMatcher, String expectedJSON, String actualJSON, String expectedMessagePattern) { 51 | try { 52 | doTest(jsonPath, arrayValueMatcher, expectedJSON, actualJSON); 53 | } 54 | catch (AssertionError e) { 55 | String failureMessage = MessageFormat.format("Exception message ''{0}'', does not match expected pattern ''{1}''", e.getMessage(), expectedMessagePattern); 56 | assertTrue(failureMessage, e.getMessage().matches(expectedMessagePattern)); 57 | return; 58 | } 59 | fail("AssertionError not thrown"); 60 | } 61 | 62 | @Test 63 | public void matchesSecondElementOfJSONObjectArray() { 64 | doTest("a", new ArrayValueMatcher(comparator, 1), "{a:[{background:grey,id:2,type:row}]}", ARRAY_OF_JSONOBJECTS); 65 | } 66 | 67 | @Test 68 | public void failsWhenSecondElementOfJSONObjectArrayDoesNotMatch() { 69 | doFailingMatchTest("a", 70 | new ArrayValueMatcher(comparator, 1), 71 | "{a:[{background:DOES_NOT_MATCH,id:2,type:row}]}", 72 | ARRAY_OF_JSONOBJECTS, 73 | "a\\[1\\]\\.background\\s*Expected:\\s*DOES_NOT_MATCH\\s*got:\\s*grey\\s*"); 74 | } 75 | 76 | @Test 77 | public void failsWhenThirdElementOfJSONObjectArrayDoesNotMatchInMultiplePlaces() { 78 | doFailingMatchTest("a", 79 | new ArrayValueMatcher(comparator, 2), 80 | "{a:[{background:DOES_NOT_MATCH,id:3,type:WRONG_TYPE}]}", 81 | ARRAY_OF_JSONOBJECTS, 82 | "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*"); 83 | } 84 | 85 | @Test 86 | public void failsWhenTwoElementsOfJSONObjectArrayDoNotMatch() { 87 | doFailingMatchTest("a", 88 | new ArrayValueMatcher(comparator, 1, 2), 89 | "{a:[{background:DOES_NOT_MATCH,id:2,type:row},{background:white,id:3,type:WRONG_TYPE}]}", 90 | ARRAY_OF_JSONOBJECTS, 91 | "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*"); 92 | } 93 | 94 | @Test 95 | public void matchesThirdElementOfSimpleValueArray() { 96 | doTest("a", new ArrayValueMatcher(comparator, 2), "{a:[3]}", ARRAY_OF_INTEGERS); 97 | } 98 | 99 | @Test 100 | public void failsWhenTwoElementOfSimpleValueArrayDoNotMatch() { 101 | doFailingMatchTest("a", new ArrayValueMatcher(comparator, 3, 4), "{a:[3,4]}", ARRAY_OF_INTEGERS, 102 | "a\\[3\\]\\s*Expected:\\s3\\s*got:\\s*4\\s*;\\s*a\\[4\\]\\s*Expected:\\s*4\\s*got:\\s*5\\s*"); 103 | } 104 | 105 | @Test 106 | public void matchesFirstElementOfArrayOfJSONArrays() { 107 | doTest("a", new ArrayValueMatcher(comparator, 0), "{a:[[6,7,8]]}", ARRAY_OF_JSONARRAYS); 108 | } 109 | 110 | @Test 111 | public void matchesSizeOfFirstThreeInnerArrays() { 112 | JSONComparator innerArraySizeComparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER); 113 | doTest("a", new ArrayValueMatcher(innerArraySizeComparator, 0, 2), "{a:[[3]]}", ARRAY_OF_JSONARRAYS); 114 | } 115 | 116 | @Test 117 | public void failsWhenInnerArraySizeDoesNotMatch() { 118 | JSONComparator innerArraySizeComparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER); 119 | doFailingMatchTest("a", 120 | new ArrayValueMatcher(innerArraySizeComparator), 121 | "{a:[[3]]}", 122 | ARRAY_OF_JSONARRAYS, 123 | "a\\[3\\]\\[\\]\\s*Expected:\\s*array size of 3 elements\\s*got:\\s*4 elements\\s*"); 124 | } 125 | 126 | @Test 127 | public void failsWhenInnerJSONObjectArrayElementDoesNotMatch() { 128 | ArrayValueMatcher innerArrayValueMatcher = new ArrayValueMatcher(comparator, 1); 129 | JSONComparator innerArrayComparator = new CustomComparator( 130 | JSONCompareMode.LENIENT, new Customization("a[2]", innerArrayValueMatcher)); 131 | doFailingMatchTest("a", 132 | new ArrayValueMatcher(innerArrayComparator, 2), // tests inner array i.e. [12,13,14] 133 | "{a:[[99]]}", 134 | ARRAY_OF_JSONARRAYS, 135 | "a\\[2\\]\\[1\\]\\s*Expected:\\s*99\\s*got:\\s*13\\s*"); 136 | } 137 | 138 | @Test 139 | public void matchesEveryElementOfJSONObjectArray() { 140 | doTest("a", new ArrayValueMatcher(comparator), "{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS); 141 | } 142 | 143 | @Test 144 | public void failsWhenNotEveryElementOfJSONObjectArrayMatches() { 145 | doFailingMatchTest("a", 146 | new ArrayValueMatcher(comparator), 147 | "{a:[{background:white}]}", 148 | ARRAY_OF_JSONOBJECTS, 149 | "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*"); 150 | } 151 | 152 | @Test 153 | public void matchesEveryElementOfJSONObjectArrayWhenRangeTooLarge() { 154 | doTest("a", new ArrayValueMatcher(comparator, 0, 500), "{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS); 155 | } 156 | 157 | @Test 158 | public void matchesElementPairsStartingFromElement1OfJSONObjectArrayWhenRangeTooLarge() { 159 | doTest("a", new ArrayValueMatcher(comparator, 1, 500), "{a:[{background:grey},{background:white}]}", ARRAY_OF_JSONOBJECTS); 160 | } 161 | 162 | @Test 163 | public void matchesElementPairsStartingFromElement0OfJSONObjectArrayWhenRangeTooLarge() { 164 | doTest("a", new ArrayValueMatcher(comparator), "{a:[{background:white},{background:grey}]}", ARRAY_OF_JSONOBJECTS); 165 | } 166 | 167 | @Test 168 | public void failsWhenAppliedToNonArray() { 169 | try { 170 | doTest("a", new ArrayValueMatcher(comparator), "{a:[{background:white}]}", "{a:{attr1:value1,attr2:value2}}"); 171 | } 172 | catch (IllegalArgumentException e) { 173 | assertEquals("Exception message", "ArrayValueMatcher applied to non-array actual value", e.getMessage()); 174 | return; 175 | } 176 | fail("Did not throw IllegalArgumentException"); 177 | } 178 | 179 | /* 180 | * Following tests verify the ability to match an element containing either 181 | * a simple value or a JSON object against simple value or JSON object 182 | * without requiring expected value to be wrapped in an array reducing 183 | * slightly the syntactic load on teh test author & reader. 184 | */ 185 | 186 | @Test 187 | public void simpleValueMatchesSecondElementOfJSONObjectArray() { 188 | doTest("a", new ArrayValueMatcher(comparator, 3), "{a:4}", ARRAY_OF_INTEGERS); 189 | } 190 | 191 | @Test 192 | public void jsonObjectMatchesSecondElementOfJSONObjectArray() { 193 | doTest("a", new ArrayValueMatcher(comparator, 1), "{a:{background:grey,id:2,type:row}}", ARRAY_OF_JSONOBJECTS); 194 | } 195 | 196 | /* 197 | * Following tests contain copies of code quoted in ArrayValueMatcher JavaDoc and are included to verify that the exact code documented works as expected. 198 | */ 199 | @Test 200 | public void verifyIdAttributeOfFirstArrayElementMatches() { 201 | JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); 202 | Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0)); 203 | JSONAssert.assertEquals("{a:[{id:1}]}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); 204 | } 205 | 206 | @Test 207 | public void verifyIdAttributeOfFirstArrayElementMatchesSimplifiedExpectedSyntax() { 208 | JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); 209 | Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0)); 210 | JSONAssert.assertEquals("{a:{id:1}}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); 211 | } 212 | 213 | @Test 214 | public void verifyTypeAttributeOfSecondAndThirdElementMatchesRow() { 215 | JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); 216 | Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1, 2)); 217 | JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); 218 | } 219 | 220 | @Test 221 | public void verifyTypeAttributeOfEveryArrayElementMatchesRow() { 222 | JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); 223 | Customization customization = new Customization("a", new ArrayValueMatcher(comparator)); 224 | JSONAssert.assertEquals("{a:[{type:row}]}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); 225 | } 226 | 227 | @Test 228 | public void verifyEveryArrayElementWithCustomComparator() { 229 | // get length of array we will verify 230 | int aLength = ((JSONArray)((JSONObject)JSONParser.parseJSON(ARRAY_OF_JSONOBJECTS)).get("a")).length(); 231 | // create array of customizations one for each array element 232 | RegularExpressionValueMatcher regExValueMatcher = new RegularExpressionValueMatcher("\\d+"); // matches one or more digits 233 | Customization[] customizations = new Customization[aLength]; 234 | for (int i=0; i regExArrayValueMatcher = new ArrayValueMatcher(regExComparator); 241 | Customization regExArrayValueCustomization = new Customization("a", regExArrayValueMatcher); 242 | CustomComparator regExCustomArrayValueComparator = new CustomComparator(JSONCompareMode.STRICT_ORDER, new Customization[] { regExArrayValueCustomization }); 243 | JSONAssert.assertEquals("{a:[{id:X}]}", ARRAY_OF_JSONOBJECTS, regExCustomArrayValueComparator); 244 | } 245 | 246 | @Test 247 | public void verifyBackgroundAttributesOfEveryArrayElementAlternateBetweenWhiteAndGrey() { 248 | JSONComparator comparator = new DefaultComparator(JSONCompareMode.LENIENT); 249 | Customization customization = new Customization("a", new ArrayValueMatcher(comparator)); 250 | JSONAssert.assertEquals("{a:[{background:white},{background:grey}]}", ARRAY_OF_JSONOBJECTS, new CustomComparator(JSONCompareMode.LENIENT, customization)); 251 | } 252 | 253 | @Test 254 | public void verifyEveryElementOfArrayIsJSONArrayOfLength3() { 255 | JSONComparator comparator = new ArraySizeComparator(JSONCompareMode.STRICT_ORDER); 256 | Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 0, 2)); 257 | JSONAssert.assertEquals("{a:[[3]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization)); 258 | } 259 | 260 | @Test 261 | public void verifySecondElementOfArrayIsJSONArrayWhoseFirstElementIs9() { 262 | Customization innerCustomization = new Customization("a[1]", new ArrayValueMatcher(comparator, 0)); 263 | JSONComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, innerCustomization); 264 | Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1)); 265 | JSONAssert.assertEquals("{a:[[9]]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization)); 266 | } 267 | 268 | @Test 269 | public void verifySecondElementOfArrayIsJSONArrayWhoseFirstElementIs9WithSimpliedExpectedString() { 270 | Customization innerCustomization = new Customization("a[1]", new ArrayValueMatcher(comparator, 0)); 271 | JSONComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, innerCustomization); 272 | Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1)); 273 | JSONAssert.assertEquals("{a:[9]}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization)); 274 | } 275 | 276 | @Test 277 | public void verifySecondElementOfArrayIsJSONArrayWhoseFirstElementIs9WithEvenMoreSimpliedExpectedString() { 278 | Customization innerCustomization = new Customization("a[1]", new ArrayValueMatcher(comparator, 0)); 279 | JSONComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, innerCustomization); 280 | Customization customization = new Customization("a", new ArrayValueMatcher(comparator, 1)); 281 | JSONAssert.assertEquals("{a:9}", ARRAY_OF_JSONARRAYS, new CustomComparator(JSONCompareMode.LENIENT, customization)); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/DependencyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import org.json.JSONObject; 18 | import org.junit.Assert; 19 | import org.junit.Test; 20 | 21 | /** 22 | * Unit tests for our external/third-party dependencies. 23 | * 24 | * @author Carter Page 25 | */ 26 | public class DependencyTest { 27 | @Test 28 | public void nop() { 29 | // Cloudbees doesn't like a unit test class with no tests 30 | } 31 | 32 | //@Test // For https://github.com/skyscreamer/JSONassert/issues/25 33 | public void testJSonGetLong() throws Exception { 34 | Long target = -4611686018427386614L; 35 | String targetString = target.toString(); 36 | 37 | JSONObject value = new JSONObject().put("id", target); 38 | Assert.assertEquals(target, (Long) value.getLong("id")); //Correct: when put as long getLong is correct 39 | 40 | value = new JSONObject().put("id", targetString); 41 | Assert.assertEquals(target, (Long) Long.parseLong(value.getString("id"))); //Correct: when put as String getString is correct 42 | Assert.assertEquals(target, (Long) value.getLong("id")); //Bug: Having json convert the string to long fails 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/JSONArrayWithNullTest.java: -------------------------------------------------------------------------------- 1 | package org.skyscreamer.jsonassert; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONObject; 5 | import org.junit.Test; 6 | 7 | public class JSONArrayWithNullTest { 8 | @Test 9 | public void testJSONArrayWithNullValue() { 10 | JSONArray jsonArray1 = getJSONArray1(); 11 | JSONArray jsonArray2 = getJSONArray2(); 12 | 13 | JSONAssert.assertEquals(jsonArray1, jsonArray2, true); 14 | JSONAssert.assertEquals(jsonArray1, jsonArray2, false); 15 | } 16 | 17 | @Test 18 | public void testJSONArrayWithNullValueAndJsonObject() { 19 | JSONArray jsonArray1 = getJSONArray1(); 20 | JSONObject jsonObject1 = new JSONObject(); 21 | jsonObject1.put("hey", "value"); 22 | 23 | JSONArray jsonArray2 = getJSONArray2(); 24 | JSONObject jsonObject2 = new JSONObject(); 25 | jsonObject2.put("hey", "value"); 26 | 27 | JSONAssert.assertEquals(jsonArray1, jsonArray2, true); 28 | JSONAssert.assertEquals(jsonArray1, jsonArray2, false); 29 | } 30 | 31 | private JSONArray getJSONArray1() { 32 | JSONArray jsonArray1 = new JSONArray(); 33 | jsonArray1.put(1); 34 | jsonArray1.put(JSONObject.NULL); 35 | jsonArray1.put(3); 36 | jsonArray1.put(2); 37 | return jsonArray1; 38 | } 39 | 40 | private JSONArray getJSONArray2() { 41 | JSONArray jsonArray1 = new JSONArray(); 42 | jsonArray1.put(1); 43 | jsonArray1.put(JSONObject.NULL); 44 | jsonArray1.put(3); 45 | jsonArray1.put(2); 46 | return jsonArray1; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/JSONCompareModeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import static junit.framework.Assert.assertEquals; 18 | import static junit.framework.Assert.assertFalse; 19 | import static junit.framework.Assert.assertTrue; 20 | import static org.skyscreamer.jsonassert.JSONCompareMode.LENIENT; 21 | import static org.skyscreamer.jsonassert.JSONCompareMode.NON_EXTENSIBLE; 22 | import static org.skyscreamer.jsonassert.JSONCompareMode.STRICT; 23 | import static org.skyscreamer.jsonassert.JSONCompareMode.STRICT_ORDER; 24 | 25 | import org.junit.Test; 26 | 27 | /** 28 | * Unit tests for {@link JSONCompareMode} 29 | */ 30 | public class JSONCompareModeTest { 31 | @Test 32 | public void testWithStrictOrdering() { 33 | assertTrue(LENIENT.withStrictOrdering(true).hasStrictOrder()); 34 | assertTrue(LENIENT.withStrictOrdering(true).isExtensible()); 35 | assertTrue(NON_EXTENSIBLE.withStrictOrdering(true).hasStrictOrder()); 36 | assertFalse(NON_EXTENSIBLE.withStrictOrdering(true).isExtensible()); 37 | 38 | assertEquals(STRICT, STRICT.withStrictOrdering(true)); 39 | assertEquals(STRICT_ORDER, STRICT_ORDER.withStrictOrdering(true)); 40 | } 41 | 42 | @Test 43 | public void testWithoutStrictOrdering() { 44 | assertFalse(STRICT_ORDER.withStrictOrdering(false).hasStrictOrder()); 45 | assertTrue(STRICT_ORDER.withStrictOrdering(false).isExtensible()); 46 | assertFalse(STRICT.withStrictOrdering(false).hasStrictOrder()); 47 | assertFalse(STRICT.withStrictOrdering(false).isExtensible()); 48 | 49 | assertEquals(LENIENT, LENIENT.withStrictOrdering(false)); 50 | assertEquals(NON_EXTENSIBLE, NON_EXTENSIBLE.withStrictOrdering(false)); 51 | } 52 | 53 | @Test 54 | public void testWithExtensibility() { 55 | assertTrue(NON_EXTENSIBLE.withExtensible(true).isExtensible()); 56 | assertFalse(NON_EXTENSIBLE.withExtensible(true).hasStrictOrder()); 57 | assertTrue(STRICT.withExtensible(true).isExtensible()); 58 | assertTrue(STRICT.withExtensible(true).hasStrictOrder()); 59 | 60 | assertEquals(LENIENT, LENIENT.withExtensible(true)); 61 | assertEquals(STRICT_ORDER, STRICT_ORDER.withExtensible(true)); 62 | } 63 | 64 | @Test 65 | public void testWithoutExtensibility() { 66 | assertFalse(STRICT_ORDER.withExtensible(false).isExtensible()); 67 | assertTrue(STRICT_ORDER.withExtensible(false).hasStrictOrder()); 68 | assertFalse(LENIENT.withExtensible(false).isExtensible()); 69 | assertFalse(LENIENT.withExtensible(false).hasStrictOrder()); 70 | 71 | assertEquals(STRICT, STRICT.withExtensible(false)); 72 | assertEquals(NON_EXTENSIBLE, NON_EXTENSIBLE.withExtensible(false)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/JSONCompareTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import static org.hamcrest.core.IsEqual.equalTo; 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertThat; 20 | import static org.junit.Assert.assertTrue; 21 | import static org.skyscreamer.jsonassert.JSONCompare.compareJSON; 22 | import static org.skyscreamer.jsonassert.JSONCompareMode.LENIENT; 23 | import static org.skyscreamer.jsonassert.JSONCompareMode.NON_EXTENSIBLE; 24 | import static org.skyscreamer.jsonassert.JSONCompareMode.STRICT; 25 | 26 | import org.hamcrest.Description; 27 | import org.hamcrest.Matcher; 28 | import org.junit.Test; 29 | import org.junit.internal.matchers.TypeSafeMatcher; 30 | 31 | /** 32 | * Unit tests for {@code JSONCompare}. 33 | */ 34 | public class JSONCompareTest { 35 | @Test 36 | public void succeedsWithEmptyArrays() { 37 | assertTrue(compareJSON("[]", "[]", LENIENT).passed()); 38 | } 39 | 40 | @Test 41 | public void reportsArraysOfUnequalLength() { 42 | JSONCompareResult result = compareJSON("[4]", "[]", LENIENT); 43 | assertThat(result, failsWithMessage(equalTo("[]: Expected 1 values but got 0"))); 44 | } 45 | 46 | @Test 47 | public void reportsArrayMissingExpectedElement() { 48 | JSONCompareResult result = compareJSON("[4]", "[7]", LENIENT); 49 | assertThat(result, failsWithMessage(equalTo("[]\nExpected: 4\n but none found\n ; []\nUnexpected: 7\n"))); 50 | assertEquals(result.getFieldMissing().size(), 1); 51 | assertEquals(result.getFieldUnexpected().size(), 1); 52 | } 53 | 54 | @Test 55 | public void reportsMismatchedFieldValues() { 56 | JSONCompareResult result = compareJSON("{\"id\": 3}", "{\"id\": 5}", LENIENT); 57 | assertThat(result, failsWithMessage(equalTo("id\nExpected: 3\n got: 5\n"))); 58 | assertThat(result, failsWithMessage(equalTo("id\nExpected: 3\n got: 5\n"))); 59 | } 60 | 61 | @Test 62 | public void reportsMissingField() { 63 | JSONCompareResult result = compareJSON("{\"obj\": {\"id\": 3}}", "{\"obj\": {}}", LENIENT); 64 | assertThat(result, failsWithMessage(equalTo("obj\nExpected: id\n but none found\n"))); 65 | assertEquals(result.getFieldMissing().size(), 1); 66 | } 67 | 68 | @Test 69 | public void reportsUnexpectedArrayWhenExpectingObject() { 70 | JSONCompareResult result = compareJSON("{}", "[]", LENIENT); 71 | assertThat(result, failsWithMessage(equalTo("\nExpected: a JSON object\n got: a JSON array\n"))); 72 | } 73 | 74 | @Test 75 | public void reportsUnexpectedObjectWhenExpectingArray() { 76 | JSONCompareResult result = compareJSON("[]", "{}", LENIENT); 77 | assertThat(result, failsWithMessage(equalTo("\nExpected: a JSON array\n got: a JSON object\n"))); 78 | } 79 | 80 | @Test 81 | public void reportsUnexpectedNull() { 82 | JSONCompareResult result = compareJSON("{\"id\": 3}", "{\"id\": null}", LENIENT); 83 | assertThat(result, failsWithMessage(equalTo("id\nExpected: 3\n got: null\n"))); 84 | } 85 | 86 | @Test 87 | public void reportsUnexpectedNonNull() { 88 | JSONCompareResult result = compareJSON("{\"id\": null}", "{\"id\": \"abc\"}", LENIENT); 89 | assertThat(result, failsWithMessage(equalTo("id\nExpected: null\n got: abc\n"))); 90 | } 91 | 92 | @Test 93 | public void reportsUnexpectedFieldInNonExtensibleMode() { 94 | JSONCompareResult result = compareJSON("{\"obj\": {}}", "{\"obj\": {\"id\": 3}}", NON_EXTENSIBLE); 95 | assertThat(result, failsWithMessage(equalTo("obj\nUnexpected: id\n"))); 96 | assertEquals(result.getFieldUnexpected().size(), 1); 97 | } 98 | 99 | @Test 100 | public void reportsMismatchedTypes() { 101 | JSONCompareResult result = compareJSON("{\"arr\":[]}", "{\"arr\":{}}", LENIENT); 102 | assertThat(result, failsWithMessage(equalTo("arr\nExpected: a JSON array\n got: a JSON object\n"))); 103 | } 104 | 105 | @Test 106 | public void reportsWrongSimpleValueCountInUnorderedArray() { 107 | JSONCompareResult result = compareJSON("[5, 5]", "[5, 7]", LENIENT); 108 | assertThat(result, failsWithMessage(equalTo("[]: Expected 2 occurrence(s) of 5 but got 1 occurrence(s) ; []\nUnexpected: 7\n"))); 109 | assertEquals(result.getFieldUnexpected().size(), 1); 110 | } 111 | 112 | @Test 113 | public void reportsMissingJSONObjectWithUniqueKeyInUnorderedArray() { 114 | JSONCompareResult result = compareJSON("[{\"id\" : 3}]", "[{\"id\" : 5}]", LENIENT); 115 | assertThat(result, failsWithMessage(equalTo("[id=3]\nExpected: a JSON object\n but none found\n ; " + 116 | "[id=5]\nUnexpected: a JSON object\n"))); 117 | assertEquals(result.getFieldMissing().size(), 1); 118 | assertEquals(result.getFieldUnexpected().size(), 1); 119 | } 120 | 121 | @Test 122 | public void reportsUnmatchedJSONObjectInUnorderedArray() { 123 | JSONCompareResult result = compareJSON("[{\"address\" : {\"street\" : \"Acacia Avenue\"}}]", "[{\"age\" : 23}]", LENIENT); 124 | assertThat(result, failsWithMessage(equalTo("[0] Could not find match for element {\"address\":{\"street\":\"Acacia Avenue\"}}"))); 125 | } 126 | 127 | @Test 128 | public void succeedsWithNestedJSONObjectsInUnorderedArray() { 129 | assertTrue(compareJSON("[{\"address\" : {\"street\" : \"Acacia Avenue\"}}, 5]", "[5, {\"address\" : {\"street\" : \"Acacia Avenue\"}}]", LENIENT).passed()); 130 | } 131 | 132 | @Test 133 | public void succeedsWithJSONObjectsWithNonUniqueKeyInUnorderedArray() { 134 | String jsonDocument = "[{\"age\" : 43}, {\"age\" : 43}]"; 135 | assertTrue(compareJSON(jsonDocument, jsonDocument, LENIENT).passed()); 136 | } 137 | 138 | @Test 139 | public void succeedsWithSomeNestedJSONObjectsInUnorderedArray() { 140 | String jsonDocument = "[{\"age\" : 43}, {\"age\" : {\"years\" : 43}}]"; 141 | assertTrue(compareJSON(jsonDocument, jsonDocument, LENIENT).passed()); 142 | } 143 | 144 | @Test 145 | public void reportsUnmatchesIntegerValueInUnorderedArrayContainingJSONObject() { 146 | JSONCompareResult result = compareJSON("[{\"address\" : {\"street\" : \"Acacia Avenue\"}}, 5]", "[{\"address\" : {\"street\" : \"Acacia Avenue\"}}, 2]", LENIENT); 147 | assertThat(result, failsWithMessage(equalTo("[1] Could not find match for element 5"))); 148 | } 149 | 150 | @Test 151 | public void reportsUnmatchedJSONArrayWhereOnlyExpectedContainsJSONObjectWithUniqueKey() { 152 | JSONCompareResult result = compareJSON("[{\"id\": 3}]", "[{}]", LENIENT); 153 | assertThat(result, failsWithMessage(equalTo("[0] Could not find match for element {\"id\":3}"))); 154 | } 155 | 156 | @Test 157 | public void reportsUnmatchedJSONArrayWhereExpectedContainsJSONObjectWithUniqueKeyButActualContainsElementOfOtherType() { 158 | JSONCompareResult result = compareJSON("[{\"id\": 3}]", "[5]", LENIENT); 159 | assertThat(result, failsWithMessage(equalTo("[0] Could not find match for element {\"id\":3}"))); 160 | } 161 | 162 | @Test 163 | public void reportsUnmatchedJSONArrayWhereExpectedContainsNonnullIntegerButActualContainsNullElement() { 164 | JSONCompareResult result = compareJSON("[ 3 ]", "[ null ]", LENIENT); 165 | assertThat(result, failsWithMessage(equalTo("[]\nExpected: 3\n but none found\n ; " + 166 | "[]\nUnexpected: null\n"))); 167 | } 168 | 169 | @Test 170 | public void reportsUnmatchedJSONArrayWhereExpectedContainsNullElementButActualContainsNonnullInteger() { 171 | JSONCompareResult result = compareJSON("[ null ]", "[ 3 ]", LENIENT); 172 | assertThat(result, failsWithMessage(equalTo("[]\nExpected: null\n but none found\n ; " + 173 | "[]\nUnexpected: 3\n"))); 174 | } 175 | 176 | @Test 177 | public void reportsStrictUnmatchedJSONArrayWhereExpectedContainsNonnullIntegerButActualContainsNullElement() { 178 | JSONCompareResult result = compareJSON("[ 3 ]", "[ null ]", STRICT); 179 | assertThat(result, failsWithMessage(equalTo("[0]\nExpected: 3\n got: null\n"))); 180 | } 181 | 182 | @Test 183 | public void reportsStrictUnmatchedJSONArrayWhereExpectedContainsNullButActualContainsNonnullInteger() { 184 | JSONCompareResult result = compareJSON("[ null ]", "[ 3 ]", STRICT); 185 | assertThat(result, failsWithMessage(equalTo("[0]\nExpected: null\n got: 3\n"))); 186 | } 187 | 188 | private Matcher failsWithMessage(final Matcher expectedMessage) { 189 | return new TypeSafeMatcher() { 190 | @Override 191 | public void describeTo(Description description) { 192 | description.appendText("a failed comparison with message ").appendDescriptionOf(expectedMessage); 193 | } 194 | 195 | @Override 196 | public boolean matchesSafely(JSONCompareResult item) { 197 | return item.failed() && expectedMessage.matches(item.getMessage()); 198 | } 199 | }; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/JSONCustomComparatorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import org.junit.Test; 18 | import org.skyscreamer.jsonassert.comparator.CustomComparator; 19 | import org.skyscreamer.jsonassert.comparator.JSONComparator; 20 | 21 | import static org.junit.Assert.assertEquals; 22 | import static org.junit.Assert.assertTrue; 23 | import static org.skyscreamer.jsonassert.JSONCompare.compareJSON; 24 | 25 | public class JSONCustomComparatorTest { 26 | 27 | String actual = "{\"first\":\"actual\", \"second\":1}"; 28 | String expected = "{\"first\":\"expected\", \"second\":1}"; 29 | 30 | String deepActual = "{\n" + 31 | " \"outer\":\n" + 32 | " {\n" + 33 | " \"inner\":\n" + 34 | " {\n" + 35 | " \"value\": \"actual\",\n" + 36 | " \"otherValue\": \"foo\"\n" + 37 | " }\n" + 38 | " }\n" + 39 | "}"; 40 | String deepExpected = "{\n" + 41 | " \"outer\":\n" + 42 | " {\n" + 43 | " \"inner\":\n" + 44 | " {\n" + 45 | " \"value\": \"expected\",\n" + 46 | " \"otherValue\": \"foo\"\n" + 47 | " }\n" + 48 | " }\n" + 49 | "}"; 50 | 51 | String simpleWildcardActual = "{\n" + 52 | " \"foo\": {\n" + 53 | " \"bar1\": {\n" + 54 | " \"baz\": \"actual\"\n" + 55 | " },\n" + 56 | " \"bar2\": {\n" + 57 | " \"baz\": \"actual\"\n" + 58 | " }\n" + 59 | " }\n" + 60 | "}"; 61 | String simpleWildcardExpected = "{\n" + 62 | " \"foo\": {\n" + 63 | " \"bar1\": {\n" + 64 | " \"baz\": \"expected\"\n" + 65 | " },\n" + 66 | " \"bar2\": {\n" + 67 | " \"baz\": \"expected\"\n" + 68 | " }\n" + 69 | " }\n" + 70 | "}"; 71 | 72 | String deepWildcardActual = "{\n" + 73 | " \"root\": {\n" + 74 | " \"baz\": \"actual\",\n" + 75 | " \"foo\": {\n" + 76 | " \"baz\": \"actual\",\n" + 77 | " \"bar\": {\n" + 78 | " \"baz\": \"actual\"\n" + 79 | " }\n" + 80 | " }\n" + 81 | " }\n" + 82 | "}"; 83 | String deepWildcardExpected = "{\n" + 84 | " \"root\": {\n" + 85 | " \"baz\": \"expected\",\n" + 86 | " \"foo\": {\n" + 87 | " \"baz\": \"expected\",\n" + 88 | " \"bar\": {\n" + 89 | " \"baz\": \"expected\"\n" + 90 | " }\n" + 91 | " }\n" + 92 | " }\n" + 93 | "}"; 94 | 95 | String rootDeepWildcardActual = "{\n" + 96 | " \"baz\": \"actual\",\n" + 97 | " \"root\": {\n" + 98 | " \"baz\": \"actual\",\n" + 99 | " \"foo\": {\n" + 100 | " \"baz\": \"actual\",\n" + 101 | " \"bar\": {\n" + 102 | " \"baz\": \"actual\"\n" + 103 | " }\n" + 104 | " }\n" + 105 | " }\n" + 106 | "}"; 107 | String rootDeepWildcardExpected = "{\n" + 108 | " \"baz\": \"expected\",\n" + 109 | " \"root\": {\n" + 110 | " \"baz\": \"expected\",\n" + 111 | " \"foo\": {\n" + 112 | " \"baz\": \"expected\",\n" + 113 | " \"bar\": {\n" + 114 | " \"baz\": \"expected\"\n" + 115 | " }\n" + 116 | " }\n" + 117 | " }\n" + 118 | "}"; 119 | 120 | int comparatorCallCount = 0; 121 | ValueMatcher comparator = new ValueMatcher() { 122 | @Override 123 | public boolean equal(Object o1, Object o2) { 124 | comparatorCallCount++; 125 | return o1.toString().equals("actual") && o2.toString().equals("expected"); 126 | } 127 | }; 128 | 129 | @Test 130 | public void whenPathMatchesInCustomizationThenCallCustomMatcher() { 131 | JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("first", comparator)); 132 | JSONCompareResult result = compareJSON(expected, actual, jsonCmp); 133 | assertTrue(result.getMessage(), result.passed()); 134 | assertEquals(1, comparatorCallCount); 135 | } 136 | 137 | @Test 138 | public void whenDeepPathMatchesCallCustomMatcher() { 139 | JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("outer.inner.value", comparator)); 140 | JSONCompareResult result = compareJSON(deepExpected, deepActual, jsonCmp); 141 | assertTrue(result.getMessage(), result.passed()); 142 | assertEquals(1, comparatorCallCount); 143 | } 144 | 145 | @Test 146 | public void whenSimpleWildcardPathMatchesCallCustomMatcher() { 147 | JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("foo.*.baz", comparator)); 148 | JSONCompareResult result = compareJSON(simpleWildcardExpected, simpleWildcardActual, jsonCmp); 149 | assertTrue(result.getMessage(), result.passed()); 150 | assertEquals(2, comparatorCallCount); 151 | } 152 | 153 | @Test 154 | public void whenDeepWildcardPathMatchesCallCustomMatcher() { 155 | JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("root.**.baz", comparator)); 156 | JSONCompareResult result = compareJSON(deepWildcardExpected, deepWildcardActual, jsonCmp); 157 | assertTrue(result.getMessage(), result.passed()); 158 | assertEquals(3, comparatorCallCount); 159 | } 160 | 161 | @Test 162 | public void whenRootDeepWildcardPathMatchesCallCustomMatcher() { 163 | JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("**.baz", comparator)); 164 | JSONCompareResult result = compareJSON(rootDeepWildcardExpected, rootDeepWildcardActual, jsonCmp); 165 | assertTrue(result.getMessage(), result.passed()); 166 | assertEquals(4, comparatorCallCount); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/RegularExpressionValueMatcherTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert; 16 | 17 | import org.junit.Assert; 18 | 19 | import org.junit.Test; 20 | import org.skyscreamer.jsonassert.comparator.CustomComparator; 21 | 22 | /** 23 | * Unit tests for RegularExpressionValueMatcher 24 | * 25 | * @author Duncan Mackinder 26 | * 27 | */ 28 | public class RegularExpressionValueMatcherTest { 29 | private static final String ARRAY_ELEMENT_PREFIX = "d.results[0].__metadata.uri"; 30 | private static final String JSON_STRING_WITH_ARRAY = "{d:{results:[{__metadata:{uri:\"http://localhost:80/Person('1')\",type:Person},id:1}]}}"; 31 | private static final String CONSTANT_URI_REGEX_EXPECTED_JSON = "{d:{results:[{__metadata:{uri:X}}]}}"; 32 | 33 | private void doTest(String jsonPath, String regex, String expectedJSON, 34 | String actualJSON) { 35 | JSONAssert.assertEquals(expectedJSON, actualJSON, new CustomComparator( 36 | JSONCompareMode.STRICT_ORDER, new Customization(jsonPath, 37 | new RegularExpressionValueMatcher(regex)))); 38 | } 39 | 40 | @Test 41 | public void constantRegexWithSimplePathMatchsStringAttribute() { 42 | doTest("a", "v.", "{a:x}", "{a:v1}"); 43 | } 44 | 45 | @Test 46 | public void constantRegexWithThreeLevelPathMatchsStringAttribute() { 47 | doTest("a.b.c", ".*Is.*", "{a:{b:{c:x}}}", "{a:{b:{c:thisIsAString}}}"); 48 | } 49 | 50 | @Test 51 | public void dynamicRegexWithSimplePathMatchsStringAttribute() { 52 | doTest("a", null, "{a:\"v.\"}", "{a:v1}"); 53 | } 54 | 55 | @Test 56 | public void dynamicRegexWithThreeLevelPathMatchsStringAttribute() { 57 | doTest("a.b.c", null, "{a:{b:{c:\".*Is.*\"}}}", 58 | "{a:{b:{c:thisIsAString}}}"); 59 | } 60 | 61 | @Test 62 | public void constantRegexMatchesStringAttributeInsideArray() { 63 | doTest(ARRAY_ELEMENT_PREFIX, "http://localhost:80/Person\\('\\d+'\\)", CONSTANT_URI_REGEX_EXPECTED_JSON, JSON_STRING_WITH_ARRAY); 64 | } 65 | 66 | @Test 67 | public void dynamicRegexMatchesStringAttributeInsideArray() { 68 | doTest(ARRAY_ELEMENT_PREFIX, null, "{d:{results:[{__metadata:{uri:\"http://localhost:80/Person\\\\('\\\\d+'\\\\)\"}}]}}", JSON_STRING_WITH_ARRAY); 69 | } 70 | 71 | @Test 72 | public void dynamicRegexMatchesStringAttributeInsideArrayWithNoArgConstructor() { 73 | JSONAssert.assertEquals("{d:{results:[{__metadata:{uri:\"http://localhost:80/Person\\\\('\\\\d+'\\\\)\"}}]}}", JSON_STRING_WITH_ARRAY, new CustomComparator( 74 | JSONCompareMode.STRICT_ORDER, new Customization(ARRAY_ELEMENT_PREFIX, 75 | new RegularExpressionValueMatcher()))); 76 | } 77 | 78 | @Test 79 | public void failsWhenDynamicRegexInvalid() { 80 | try { 81 | doTest(ARRAY_ELEMENT_PREFIX, null, "{d:{results:[{__metadata:{uri:\"http://localhost:80/Person('\\\\d+'\\\\)\"}}]}}", JSON_STRING_WITH_ARRAY); 82 | } 83 | catch (AssertionError e) { 84 | Assert.assertTrue("Invalid exception message returned: "+ e.getMessage(), e.getMessage().startsWith(ARRAY_ELEMENT_PREFIX + ": Dynamic expected pattern invalid: ")); 85 | } 86 | } 87 | 88 | @Test 89 | public void failsWhenDynamicRegexDoesNotMatchStringAttributeInsideArray() { 90 | try { 91 | doTest(ARRAY_ELEMENT_PREFIX, null, "{d:{results:[{__metadata:{uri:\"http://localhost:80/Person\\\\('\\\\w+'\\\\)\"}}]}}", JSON_STRING_WITH_ARRAY); 92 | } 93 | catch (AssertionError e) { 94 | Assert.assertTrue("Invalid exception message returned: "+ e.getMessage(), e.getMessage().startsWith(ARRAY_ELEMENT_PREFIX + ": Dynamic expected pattern did not match value")); 95 | } 96 | } 97 | 98 | @Test 99 | public void failsWhenConstantRegexInvalid() { 100 | try { 101 | doTest(ARRAY_ELEMENT_PREFIX, "http://localhost:80/Person\\\\['\\\\d+'\\\\)", CONSTANT_URI_REGEX_EXPECTED_JSON, JSON_STRING_WITH_ARRAY); 102 | } 103 | catch (IllegalArgumentException e) { 104 | Assert.assertTrue("Invalid exception message returned: "+ e.getMessage(), e.getMessage().startsWith("Constant expected pattern invalid: ")); 105 | } 106 | } 107 | 108 | @Test 109 | public void failsWhenConstantRegexDoesNotMatchStringAttributeInsideArray() { 110 | try { 111 | doTest(ARRAY_ELEMENT_PREFIX, "http://localhost:80/Person\\\\('\\\\w+'\\\\)", CONSTANT_URI_REGEX_EXPECTED_JSON, JSON_STRING_WITH_ARRAY); 112 | } 113 | catch (AssertionError e) { 114 | Assert.assertTrue("Invalid exception message returned: "+ e.getMessage(), e.getMessage().startsWith(ARRAY_ELEMENT_PREFIX + ": Constant expected pattern did not match value")); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/comparator/ArraySizeComparatorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert.comparator; 16 | 17 | import static org.junit.Assert.assertTrue; 18 | import static org.junit.Assert.fail; 19 | 20 | import java.text.MessageFormat; 21 | 22 | import org.junit.Test; 23 | import org.skyscreamer.jsonassert.JSONAssert; 24 | import org.skyscreamer.jsonassert.JSONCompareMode; 25 | 26 | /** 27 | * Unit tests for ArraySizeComparator 28 | * 29 | * @author Duncan Mackinder 30 | * 31 | */ 32 | public class ArraySizeComparatorTest { 33 | private static final String twoElementArray = "{a:[b,c]}"; 34 | 35 | private void doTest(String expectedJSON, String actualJSON) 36 | { 37 | JSONAssert.assertEquals(expectedJSON, actualJSON, new ArraySizeComparator(JSONCompareMode.STRICT_ORDER)); 38 | } 39 | 40 | private void doFailingMatchTest(String expectedJSON, String actualJSON, String expectedMessagePattern) { 41 | try { 42 | doTest(expectedJSON, actualJSON); 43 | } 44 | catch (AssertionError e) { 45 | String failureMessage = MessageFormat.format("Exception message ''{0}'', does not match expected pattern ''{1}''", e.getMessage(), expectedMessagePattern); 46 | assertTrue(failureMessage, e.getMessage().matches(expectedMessagePattern)); 47 | return; 48 | } 49 | fail("AssertionError not thrown"); 50 | } 51 | 52 | @Test 53 | public void succeedsWhenExactSizeExpected() { 54 | doTest("{a:[2]}", twoElementArray); 55 | } 56 | 57 | @Test 58 | public void succeedsWhenSizeWithinExpectedRange() { 59 | doTest("{a:[1,3]}", twoElementArray); 60 | } 61 | 62 | @Test 63 | public void succeedsWhenSizeIsMinimumOfExpectedRange() { 64 | doTest("{a:[2,4]}", twoElementArray); 65 | } 66 | 67 | @Test 68 | public void succeedsWhenSizeIsMaximumOfExpectedRange() { 69 | doTest("{a:[1,2]}", twoElementArray); 70 | } 71 | 72 | @Test 73 | public void failsWhenExpectedArrayTooShort() { 74 | doFailingMatchTest("{a:[]}", twoElementArray, "a\\[\\]: invalid expectation: expected array should contain either 1 or 2 elements but contains 0 elements"); 75 | } 76 | 77 | @Test 78 | public void failsWhenExpectedArrayTooLong() { 79 | doFailingMatchTest("{a:[1,2,3]}", twoElementArray, "a\\[\\]: invalid expectation: expected array should contain either 1 or 2 elements but contains 3 elements"); 80 | } 81 | 82 | @Test 83 | public void failsWhenExpectedNotAllSimpleTypes() { 84 | doFailingMatchTest("{a:[{y:1},2]}", twoElementArray, "a\\[\\]: invalid expectation: minimum expected array size '\\{\"y\":1\\}' not a number"); 85 | } 86 | 87 | @Test 88 | public void failsWhenExpectedMinimumTooSmall() { 89 | doFailingMatchTest("{a:[-1,6]}", twoElementArray, "a\\[\\]: invalid expectation: minimum expected array size '-1' negative"); 90 | } 91 | 92 | @Test 93 | public void failsWhenExpectedMaximumTooSmall() { 94 | doFailingMatchTest("{a:[8,6]}", twoElementArray, "a\\[\\]: invalid expectation: maximum expected array size '6' less than minimum expected array size '8'"); 95 | } 96 | 97 | @Test 98 | public void failsWhenExpectedArraySizeNotANumber() { 99 | doFailingMatchTest("{a:[X]}", twoElementArray, "a\\[\\]: invalid expectation: expected array size 'X' not a number"); 100 | } 101 | 102 | @Test 103 | public void failsWhenFirstExpectedArrayElementNotANumber() { 104 | doFailingMatchTest("{a:[MIN,6]}", twoElementArray, "a\\[\\]: invalid expectation: minimum expected array size 'MIN' not a number"); 105 | } 106 | 107 | @Test 108 | public void failsWhenSecondExpectedArrayElementNotANumber() { 109 | doFailingMatchTest("{a:[8,MAX]}", twoElementArray, "a\\[\\]: invalid expectation: maximum expected array size 'MAX' not a number"); 110 | } 111 | 112 | @Test 113 | public void failsWhenActualArrayTooShort() { 114 | doFailingMatchTest("{a:[3]}", twoElementArray, "a\\[\\]\\s*Expected:\\s*array size of 3 elements\\s*got:\\s*2 elements\\s*"); 115 | } 116 | 117 | @Test 118 | public void failsWhenActualArrayLongerThanExpectedLength() { 119 | doFailingMatchTest("{a:[1]}", twoElementArray, "a\\[\\]\\s*Expected:\\s*array size of 1 elements\\s*got:\\s*2 elements\\s*"); 120 | } 121 | 122 | @Test 123 | public void failsWhenActualArrayLongerThanMaxOfExpectedRange() { 124 | doFailingMatchTest("{a:[0,1]}", twoElementArray, "a\\[\\]\\s*Expected:\\s*array size of 0 to 1 elements\\s*got:\\s*2 elements\\s*"); 125 | } 126 | 127 | /* 128 | * Following tests are copied from ArraySizeComparator JavaDoc and are include to ensure code as documented work as expected. 129 | */ 130 | 131 | @Test 132 | public void succeedsWhenActualArrayContainsExactly3Elements() { 133 | JSONAssert.assertEquals("{a:[3]}", "{a:[7, 8, 9]}", new ArraySizeComparator(JSONCompareMode.LENIENT)); 134 | } 135 | 136 | @Test 137 | public void succeedsWhenActualArrayContainsBetween2And6Elements() { 138 | JSONAssert.assertEquals("{a:[2,6]}", "{a:[7, 8, 9]}", new ArraySizeComparator(JSONCompareMode.LENIENT)); 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/comparator/CustomComparatorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert.comparator; 16 | 17 | import junit.framework.Assert; 18 | import org.json.JSONArray; 19 | import org.junit.Test; 20 | import org.skyscreamer.jsonassert.JSONCompare; 21 | import org.skyscreamer.jsonassert.JSONCompareMode; 22 | import org.skyscreamer.jsonassert.JSONCompareResult; 23 | 24 | /** 25 | * @author Ivan Zaytsev 26 | * 2013-01-04 27 | */ 28 | public class CustomComparatorTest { 29 | 30 | private static class ArrayOfJsonObjectsComparator extends DefaultComparator { 31 | public ArrayOfJsonObjectsComparator(JSONCompareMode mode) { 32 | super(mode); 33 | } 34 | 35 | @Override 36 | public void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) { 37 | compareJSONArrayOfJsonObjects(prefix, expected, actual, result); 38 | } 39 | } 40 | 41 | @Test 42 | public void testFullArrayComparison() throws Exception { 43 | JSONCompareResult compareResult = JSONCompare.compareJSON( 44 | "[{id:1}, {id:3}, {id:5}]", 45 | "[{id:1}, {id:3}, {id:6}, {id:7}]", new ArrayOfJsonObjectsComparator(JSONCompareMode.LENIENT) 46 | ); 47 | 48 | Assert.assertTrue(compareResult.failed()); 49 | String message = compareResult.getMessage().replaceAll("\n", ""); 50 | Assert.assertTrue(message, message.matches(".*id=5.*Expected.*id=6.*Unexpected.*id=7.*Unexpected.*")); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/org/skyscreamer/jsonassert/comparator/JSONCompareUtilTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Unless required by applicable law or agreed to in writing, software 9 | * distributed under the License is distributed on an "AS IS" BASIS, 10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | * See the License for the specific language governing permissions and 12 | * limitations under the License. 13 | */ 14 | 15 | package org.skyscreamer.jsonassert.comparator; 16 | 17 | import junit.framework.Assert; 18 | import org.junit.Test; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | /** 26 | * Test JSONCompareUtil 27 | * 28 | * @author Carter Page 29 | */ 30 | public class JSONCompareUtilTest { 31 | @Test 32 | public void testGetCardinalityMap() { 33 | final int NUM_A = 76; 34 | final int NUM_B = 3; 35 | final int NUM_C = 0; 36 | final int NUM_D = 1; 37 | final int NUM_E = 2; 38 | 39 | List listToTest = new ArrayList(NUM_A + NUM_B + NUM_C + NUM_D + NUM_E); 40 | for (int i = 0; i < NUM_A; ++i) listToTest.add("A"); 41 | for (int i = 0; i < NUM_B; ++i) listToTest.add("B"); 42 | for (int i = 0; i < NUM_C; ++i) listToTest.add("C"); 43 | for (int i = 0; i < NUM_D; ++i) listToTest.add("D"); 44 | for (int i = 0; i < NUM_E; ++i) listToTest.add("E"); 45 | Collections.shuffle(listToTest); 46 | 47 | Map cardinalityMap = JSONCompareUtil.getCardinalityMap(listToTest); 48 | Assert.assertEquals(NUM_A, cardinalityMap.get("A").intValue()); 49 | Assert.assertEquals(NUM_B, cardinalityMap.get("B").intValue()); 50 | Assert.assertNull(cardinalityMap.get("C")); 51 | Assert.assertEquals(NUM_D, cardinalityMap.get("D").intValue()); 52 | Assert.assertEquals(NUM_E, cardinalityMap.get("E").intValue()); 53 | } 54 | } 55 | --------------------------------------------------------------------------------