Permalink
Browse files

structural and value differences

Classes in net.javacrumbs.jsonunit.differences added to
collect and report value and structure difference.
  • Loading branch information...
1 parent 7be5bbe commit 859802186ae9d288903189ec6226adbe792b2f05 @nopolabs committed May 12, 2012
View
@@ -59,15 +59,15 @@ For example
Results in
- java.lang.AssertionError: JSON documents are different:
- Different keys found in node "". Expected [root2, root3, test], got [root4, test].
- Different value found in node "test[0]". Expected 1, got 5.
- Different values found in node "test[1]". Expected '2', got 'false'.
- Different keys found in node "test[2]". Expected [child], got [child, child2].
- Different value found in node "test[2].child.value1". Expected 1, got 5.
- Different values found in node "test[2].child.value2". Expected 'true', got '"true"'.
- Different keys found in node "test[2].child.value4". Expected [leaf], got [leaf2].
-
+ JSON documents have different structures:
+ Different keys found in node "". Expected [root2, root3, test], got [root4, test].
+ Different keys found in node "test[2]". Expected [child], got [child, child2].
+ Different keys found in node "test[2].child.value4". Expected [leaf], got [leaf2].
+ JSON documents have different values:
+ Different value found in node "test[0]". Expected 1, got 5.
+ Different values found in node "test[1]". Expected '2', got 'false'.
+ Different value found in node "test[2].child.value1". Expected 1, got 5.
+ Different values found in node "test[2].child.value2". Expected 'true', got '"true"'.
Maven dependency
@@ -29,6 +29,10 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import net.javacrumbs.jsonunit.differences.Differences;
+import net.javacrumbs.jsonunit.differences.StructureDifferences;
+import net.javacrumbs.jsonunit.differences.ValueDifferences;
+
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
@@ -41,29 +45,33 @@
*/
class Diff {
private static final Pattern ARRAY_PATTERN = Pattern.compile("(\\w+)\\[(\\d+)\\]");
- private final JsonNode expectedRoot;
+ private final JsonNode expectedRoot;
private final JsonNode actualRoot;
- private final List<String> differences = new ArrayList<String>();
- private final String startPath;
-
+ private final Differences structureDifferences = new StructureDifferences();
+ private final Differences valueDifferences = new ValueDifferences();
+ private final String startPath;
+ private boolean compared = false;
+
private enum NodeType {OBJECT, ARRAY, STRING, NUMBER, BOOLEAN, NULL};
-
+
public Diff(JsonNode expected, JsonNode actual, String startPath) {
super();
this.expectedRoot = expected;
this.actualRoot = actual;
this.startPath = startPath;
}
-
-
-
+
+
private void compare() {
- JsonNode part = getStartNode(actualRoot, startPath);
- if (part.isMissingNode()) {
- differenceFound("No value found in path \"%s\".", startPath);
- } else {
- compareNodes(expectedRoot, part, startPath);
+ if ( ! compared) {
+ JsonNode part = getStartNode(actualRoot, startPath);
+ if (part.isMissingNode()) {
+ structureDifferenceFound("Missing node in path \"%s\".", startPath);
+ } else {
+ compareNodes(expectedRoot, part, startPath);
+ }
+ compared = true;
}
}
@@ -97,9 +105,9 @@ static JsonNode getStartNode(JsonNode actualRoot, String startPath) {
private void compareObjectNodes(ObjectNode expected, ObjectNode actual, String path) {
Map<String, JsonNode> expectedFields = getFields(expected);
Map<String, JsonNode> actualFields = getFields(actual);
-
+
if (!expectedFields.keySet().equals(actualFields.keySet())) {
- differenceFound("Different keys found in node \"%s\". Expected %s, got %s.", path, sort(expectedFields.keySet()), sort(actualFields.keySet()));
+ structureDifferenceFound("Different keys found in node \"%s\". Expected %s, got %s.", path, sort(expectedFields.keySet()), sort(actualFields.keySet()));
}
for (String fieldName : commonFields(expectedFields, actualFields)) {
@@ -122,7 +130,7 @@ private void compareNodes(JsonNode expectedNode, JsonNode actualNode, String fie
NodeType expectedNodeType = getNodeType(expectedNode);
NodeType actualNodeType = getNodeType(actualNode);
if (!expectedNodeType.equals(actualNodeType)) {
- differenceFound("Different values found in node \"%s\". Expected '%s', got '%s'.", fieldPath, expectedNode, actualNode);
+ valueDifferenceFound("Different values found in node \"%s\". Expected '%s', got '%s'.", fieldPath, expectedNode, actualNode);
} else {
switch (expectedNodeType) {
case OBJECT:
@@ -153,17 +161,17 @@ private void compareNodes(JsonNode expectedNode, JsonNode actualNode, String fie
private void compareValues(Object expectedValue, Object actualValue, String path) {
if (!expectedValue.equals(actualValue)) {
- differenceFound("Different value found in node \"%s\". Expected %s, got %s.", path, expectedValue, actualValue);
+ valueDifferenceFound("Different value found in node \"%s\". Expected %s, got %s.", path, expectedValue, actualValue);
}
}
private void compareArrayNodes(ArrayNode expectedNode, ArrayNode actualNode, String path) {
- List<JsonNode> expectedElements = asList(expectedNode.getElements());
- List<JsonNode> actualElements = asList(actualNode.getElements());
+ List<JsonNode> expectedElements = asList(expectedNode.getElements());
+ List<JsonNode> actualElements = asList(actualNode.getElements());
if (expectedElements.size()!=actualElements.size()) {
- differenceFound("Array \"%s\" has different length. Expected %d, got %d.", path, expectedElements.size(), actualElements.size());
- }
+ structureDifferenceFound("Array \"%s\" has different length. Expected %d, got %d.", path, expectedElements.size(), actualElements.size());
+ }
for (int i=0; i<Math.min(expectedElements.size(), actualElements.size()); i++) {
compareNodes(expectedElements.get(i), actualElements.get(i), getArrayPath(path, i));
}
@@ -220,7 +228,7 @@ private String getPath(String parent, String name) {
return parent+"."+name;
}
}
-
+
/**
* Constructs path to an array element.
* @param path
@@ -235,8 +243,12 @@ private String getArrayPath(String parent, int i) {
}
}
- private void differenceFound(String message, Object... arguments) {
- differences.add(String.format(message, arguments));
+ private void structureDifferenceFound(String message, Object... arguments) {
+ structureDifferences.add(message, arguments);
+ }
+
+ private void valueDifferenceFound(String message, Object... arguments) {
+ valueDifferences.add(message, arguments);
}
@@ -251,11 +263,15 @@ private void differenceFound(String message, Object... arguments) {
return new TreeSet<String>(set);
}
- public boolean similar() {
+ public boolean similarStructure() {
compare();
- return differences.isEmpty();
+ return structureDifferences.isEmpty();
+ }
+
+ public boolean similar() {
+ return similarStructure() && valueDifferences.isEmpty();
}
-
+
/**
* Returns children of an ObjectNode.
* @param node
@@ -270,17 +286,38 @@ public boolean similar() {
}
return Collections.unmodifiableMap(result);
}
-
+
@Override
public String toString() {
- if (differences.isEmpty()) {
- return "JSON documents have the same value.";
- } else {
- StringBuilder message = new StringBuilder("JSON documents are different:\n");
- for (String difference : differences) {
- message.append(difference).append("\n");
- }
- return message.toString();
+ return differences();
+ }
+
+ public String differences() {
+ if (similar()) {
+ return "JSON documents have the same value.";
}
+ StringBuilder message = new StringBuilder();
+ structureDifferences.appendDifferences(message);
+ valueDifferences.appendDifferences(message);
+ return message.toString();
}
+
+ public String valueDifferences() {
+ if (similarStructure()) {
+ return "JSON documents have the same value.";
+ }
+ StringBuilder message = new StringBuilder();
+ valueDifferences.appendDifferences(message);
+ return message.toString();
+ }
+
+ public String structureDifferences() {
+ if (similarStructure()) {
+ return "JSON documents have the same structure.";
+ }
+ StringBuilder message = new StringBuilder();
+ structureDifferences.appendDifferences(message);
+ return message.toString();
+ }
+
}
@@ -23,27 +23,27 @@
import org.codehaus.jackson.map.ObjectMapper;
/**
- * Assertions for comparing JSON. The comparison ignores whitespaces and order of nodes.
+ * Assertions for comparing JSON. The comparison ignores whitespaces and order of nodes.
* @author Lukas Krecan
*
*/
public class JsonAssert {
-
+
private static final ObjectMapper MAPPER = new ObjectMapper();
-
+
private JsonAssert(){
//nothing
}
-
+
/**
- * Compares to JSON documents. Throws {@link AssertionError} if they are different.
+ * Compares two JSON documents. Throws {@link AssertionError} if they are different.
* @param expected
* @param actual
*/
public static void assertJsonEquals(String expected, String actual) {
assertJsonEquals(new StringReader(expected), new StringReader(actual));
}
-
+
/**
* Compares to JSON documents. Throws {@link AssertionError} if they are different.
* @param expected
@@ -63,7 +63,7 @@ public static void assertJsonEquals(Reader expected, Reader actual) {
public static void assertJsonEquals(JsonNode expectedNode, JsonNode actualNode) {
assertJsonPartEquals(expectedNode, actualNode, "");
}
-
+
/**
* Compares part of the JSON. Path has this format "root.array[0].value".
* @param expected
@@ -88,7 +88,7 @@ public static void assertJsonPartEquals(Reader expected, Reader fullJson, String
JsonNode fullJsonNode = readValue(fullJson, "fullJson");
assertJsonPartEquals(expectedNode, fullJsonNode, path);
}
-
+
/**
* Compares part of the JSON. Path has this format "root.array[0].value".
* @param expected
@@ -99,7 +99,75 @@ public static void assertJsonPartEquals(String expected, String fullJson, String
assertJsonPartEquals(new StringReader(expected), new StringReader(fullJson), path);
}
-
+
+ /**
+ * Compares structures of two JSON documents.
+ * Throws {@link AssertionError} if they are different.
+ * @param expected
+ * @param actual
+ */
+ public static void assertJsonStructureEquals(String expected, String actual) {
+ assertJsonStructureEquals(new StringReader(expected), new StringReader(actual));
+ }
+
+ /**
+ * Compares structures of two JSON documents.
+ * Throws {@link AssertionError} if they are different.
+ * @param expected
+ * @param actual
+ */
+ public static void assertJsonStructureEquals(Reader expected, Reader actual) {
+ JsonNode expectedNode = readValue(expected, "expected");
+ JsonNode actualNode = readValue(actual, "actual");
+ assertJsonStructureEquals(expectedNode, actualNode);
+ }
+
+ /**
+ * Compares structures of two JSON documents.
+ * Throws {@link AssertionError} if they are different.
+ * @param expectedNode
+ * @param actualNode
+ */
+ public static void assertJsonStructureEquals(JsonNode expectedNode, JsonNode actualNode) {
+ assertJsonStructurePartEquals(expectedNode, actualNode, "");
+ }
+
+ /**
+ * Compares structure of part of the JSON. Path has this format "root.array[0].value".
+ * @param expected
+ * @param fullJson
+ * @param path
+ */
+ public static void assertJsonStructurePartEquals(JsonNode expected, JsonNode fullJson, String path) {
+ Diff diff = new Diff(expected, fullJson, path);
+ if (!diff.similarStructure()) {
+ doFail(diff.structureDifferences());
+ }
+ }
+
+ /**
+ * Compares structure of part of the JSON. Path has this format "root.array[0].value".
+ * @param expected
+ * @param fullJson
+ * @param path
+ */
+ public static void assertJsonStructurePartEquals(Reader expected, Reader fullJson, String path) {
+ JsonNode expectedNode = readValue(expected, "expected");
+ JsonNode fullJsonNode = readValue(fullJson, "fullJson");
+ assertJsonStructurePartEquals(expectedNode, fullJsonNode, path);
+ }
+
+ /**
+ * Compares structure of part of the JSON. Path has this format "root.array[0].value".
+ * @param expected
+ * @param fullJson
+ * @param path
+ */
+ public static void assertJsonStructurePartEquals(String expected, String fullJson, String path) {
+ assertJsonStructurePartEquals(new StringReader(expected), new StringReader(fullJson), path);
+ }
+
+
private static JsonNode readValue(Reader value, String label) {
try {
return MAPPER.readTree(value);
@@ -113,6 +181,6 @@ private static JsonNode readValue(Reader value, String label) {
private static void doFail(String diffMessage) {
throw new AssertionError(diffMessage);
}
-
-
+
+
}
@@ -0,0 +1,40 @@
+package net.javacrumbs.jsonunit.differences;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AbstractDifferences implements Differences {
+
+ private String differenceType;
+ private List<String> messages = new ArrayList<String>();
+
+ protected AbstractDifferences(String differenceType) {
+ this.differenceType = differenceType;
+ }
+
+ public String getDifferenceType() {
+ return differenceType;
+ }
+
+ public void add(String message, Object... args) {
+ add(String.format(message, args));
+ }
+
+ public void add(String message) {
+ messages.add(message);
+ }
+
+ public boolean isEmpty() {
+ return messages.isEmpty();
+ }
+
+ public void appendDifferences(StringBuilder builder) {
+ if ( ! messages.isEmpty()) {
+ builder.append("JSON documents have different " + getDifferenceType() + ":\n");
+ for (String message : messages) {
+ builder.append(message).append("\n");
+ }
+ }
+ }
+
+}
Oops, something went wrong.

0 comments on commit 8598021

Please sign in to comment.