diff --git a/pom.xml b/pom.xml index fe8000b..6703937 100644 --- a/pom.xml +++ b/pom.xml @@ -111,6 +111,11 @@ slf4j-api 1.8.0-beta2 + + com.jayway.jsonpath + json-path + 2.4.0 + @@ -224,4 +229,4 @@ - \ No newline at end of file + diff --git a/src/main/java/io/github/jsonSnapshot/Snapshot.java b/src/main/java/io/github/jsonSnapshot/Snapshot.java index 9d73e9a..fc51ab2 100644 --- a/src/main/java/io/github/jsonSnapshot/Snapshot.java +++ b/src/main/java/io/github/jsonSnapshot/Snapshot.java @@ -1,13 +1,21 @@ package io.github.jsonSnapshot; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.spi.json.JacksonJsonProvider; +import org.assertj.core.util.diff.DiffUtils; +import org.assertj.core.util.diff.Patch; + import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; -import org.assertj.core.util.diff.DiffUtils; -import org.assertj.core.util.diff.Patch; +import static io.github.jsonSnapshot.SnapshotMatcher.defaultJsonFunction; public class Snapshot { @@ -19,6 +27,8 @@ public class Snapshot { private Function jsonFunction; + private final List pathsToIgnore = new ArrayList<>(); + private Object[] current; Snapshot( @@ -65,11 +75,11 @@ private SnapshotMatchException generateDiffError(String rawSnapshot, String curr + currentObject.trim() + "\n\n" + patch - .getDeltas() - .stream() - .map(delta -> delta.toString() + "\n") - .reduce(String::concat) - .get(); + .getDeltas() + .stream() + .map(delta -> delta.toString() + "\n") + .reduce(String::concat) + .get(); return new SnapshotMatchException(error); } @@ -83,11 +93,39 @@ private String getRawSnapshot(Collection rawSnapshots) { return null; } + private Object ignorePaths(Object input) { + DocumentContext context = JsonPath.using(new JacksonJsonProvider()).parse(defaultJsonFunction().apply(input)); + this.pathsToIgnore.forEach(path -> context.delete(path)); + String root = "$"; + return context.read(root); + } + + private Object[] getCurrentWithoutIgnoredPaths() { + if (pathsToIgnore.isEmpty() || current == null) { + return current; + } + + return Arrays.asList(current).stream() + .map(this::ignorePaths) + .collect(Collectors.toList()) + .toArray(); + } + private String takeSnapshot() { - return getSnapshotName() + jsonFunction.apply(current); + return getSnapshotName() + jsonFunction.apply(getCurrentWithoutIgnoredPaths()); } public String getSnapshotName() { return clazz.getName() + "." + method.getName() + "="; } + + /** + * Ignore fields when comparing object and snapshot. + * + * @param jsonPathsToIgnore A list of paths to ignore in JsonPath syntax. + */ + public Snapshot ignoring(String... jsonPathsToIgnore) { + this.pathsToIgnore.addAll(Arrays.asList(jsonPathsToIgnore)); + return this; + } } diff --git a/src/test/java/io/github/jsonSnapshot/SnapshotIntegrationTest.java b/src/test/java/io/github/jsonSnapshot/SnapshotIntegrationTest.java index fc519c8..a295c06 100644 --- a/src/test/java/io/github/jsonSnapshot/SnapshotIntegrationTest.java +++ b/src/test/java/io/github/jsonSnapshot/SnapshotIntegrationTest.java @@ -73,4 +73,28 @@ void shouldThrowStackOverflowError() { assertThrows(SnapshotMatchException.class, () -> expect(fakeObject1).toMatchSnapshot()); } + + @Test + public void shouldMatchSnapshotAndIgnoreTopLevelFieldMethod() { + expect(FakeObject.builder().id("anyId6").value(6).name("anyName6").build()) + .ignoring("value") + .toMatchSnapshot(); + } + + @Test + public void shouldMatchSnapshotAndIgnoreSeveralNestedFieldsMethod() { + FakeObject grandChild = FakeObject.builder().id("grandChild7").value(7).build(); + FakeObject child = FakeObject.builder().id("child7").value(7).fakeObject(grandChild).build(); + expect(FakeObject.builder().id("anyId7").value(7).name("anyName7").fakeObject(child).build()) + .ignoring("value", "fakeObject.id", "fakeObject.fakeObject") + .toMatchSnapshot(); + } + + @Test + public void shouldMatchSnapshotAndIgnoreEverythingMethod() { + FakeObject child = FakeObject.builder().id("child8").value(8).build(); + expect(FakeObject.builder().id("anyId8").value(8).name("anyName8").fakeObject(child).build()) + .ignoring("$..*") + .toMatchSnapshot(); + } } diff --git a/src/test/java/io/github/jsonSnapshot/SnapshotIntegrationTest.snap b/src/test/java/io/github/jsonSnapshot/SnapshotIntegrationTest.snap index 1887a15..864782c 100644 --- a/src/test/java/io/github/jsonSnapshot/SnapshotIntegrationTest.snap +++ b/src/test/java/io/github/jsonSnapshot/SnapshotIntegrationTest.snap @@ -1,3 +1,27 @@ +io.github.jsonSnapshot.SnapshotIntegrationTest.shouldMatchSnapshotAndIgnoreEverythingMethod=[ + { } +] + + +io.github.jsonSnapshot.SnapshotIntegrationTest.shouldMatchSnapshotAndIgnoreSeveralNestedFieldsMethod=[ + { + "fakeObject": { + "value": 7 + }, + "id": "anyId7", + "name": "anyName7" + } +] + + +io.github.jsonSnapshot.SnapshotIntegrationTest.shouldMatchSnapshotAndIgnoreTopLevelFieldMethod=[ + { + "id": "anyId6", + "name": "anyName6" + } +] + + io.github.jsonSnapshot.SnapshotIntegrationTest.shouldMatchSnapshotFour=[ { "id": "anyId4", diff --git a/src/test/java/io/github/jsonSnapshot/SnapshotTest.java b/src/test/java/io/github/jsonSnapshot/SnapshotTest.java index 4dc3e26..44a2925 100644 --- a/src/test/java/io/github/jsonSnapshot/SnapshotTest.java +++ b/src/test/java/io/github/jsonSnapshot/SnapshotTest.java @@ -6,10 +6,15 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Collections; import java.util.TreeSet; import java.util.stream.Collectors; import java.util.stream.Stream; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.jsonpath.PathNotFoundException; +import org.junit.Before; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -31,13 +36,7 @@ class SnapshotTest { @BeforeEach void setUp() throws NoSuchMethodException, IOException { snapshotFile = new SnapshotFile(DEFAULT_CONFIG.getFilePath(), "anyFilePath"); - snapshot = - new Snapshot( - snapshotFile, - String.class, - String.class.getDeclaredMethod("toString"), - SnapshotMatcher.defaultJsonFunction(), - "anyObject"); + snapshot = createSnapshot("anyObject"); } @AfterEach @@ -45,6 +44,15 @@ void tearDown() throws IOException { Files.delete(Paths.get(FILE_PATH)); } + private Snapshot createSnapshot(Object... current) throws NoSuchMethodException { + return new Snapshot( + snapshotFile, + String.class, + String.class.getDeclaredMethod("toString"), + SnapshotMatcher.defaultJsonFunction(), + current); + } + @Test void shouldGetSnapshotNameSuccessfully() { String snapshotName = snapshot.getSnapshotName(); @@ -64,4 +72,39 @@ void shouldMatchSnapshotWithException() { assertThrows(SnapshotMatchException.class, snapshot::toMatchSnapshot); } + + @Test + public void shouldNotFailOnEmptyIgnoredPathList() { + snapshot.ignoring().toMatchSnapshot(); + assertThat(snapshotFile.getRawSnapshots()).isEqualTo(Stream.of(SNAPSHOT).collect(Collectors.toCollection(TreeSet::new))); + } + + @Test + public void shouldFailWhenTryingToIgnoreChildOfPrimitiveJsonObject() { + assertThrows(PathNotFoundException.class, () -> snapshot.ignoring("anyObject_is_a_json_string_and_can_not_have_children").toMatchSnapshot()); + } + + @Test + public void shouldIgnoreTopLevelAndNestedFields() throws NoSuchMethodException, IOException { + Object object = new ObjectMapper().readTree("{\n" + + " \"notIgnored\": \"notIgnored\",\n" + + " \"ignoredInParent\": \"ignored\",\n" + + " \"child\": {\n" + + " \"ignoredInParent\": \"notIgnored\",\n" + + " \"ignoredInChild\": \"ignored\"\n" + + " }\n" + + "}"); + Snapshot snapshot = createSnapshot(object); + + snapshot.ignoring("ignoredInParent", "child.ignoredInChild").toMatchSnapshot(); + String expectedSnapshot = "java.lang.String.toString=[\n" + + " {\n" + + " \"child\": {\n" + + " \"ignoredInParent\": \"notIgnored\"\n" + + " },\n" + + " \"notIgnored\": \"notIgnored\"\n" + + " }\n" + + "]"; + assertThat(snapshotFile.getRawSnapshots()).isEqualTo(Collections.singleton(expectedSnapshot)); + } }