From 4daf3f1d2c0f2068024f5b9c27593f131424dd4c Mon Sep 17 00:00:00 2001 From: Juja Date: Thu, 12 Nov 2020 17:59:27 +0530 Subject: [PATCH 01/13] Initial work-in-progress commit to enable support for custom snapshot comparators and reporters --- build.gradle | 10 +- .../au/com/origin/snapshots/Snapshot.java | 140 ++++++++++++------ .../com/origin/snapshots/SnapshotConfig.java | 50 +++++-- .../origin/snapshots/SnapshotVerifier.java | 3 +- .../snapshots/comparators/CompareResult.java | 16 ++ .../snapshots/comparators/CompareUtils.java | 14 ++ .../PlainTextEqualsComparator.java | 23 +++ .../comparators/SnapshotComparator.java | 5 + ...ctComparatorAwareSnapshotDiffReporter.java | 32 ++++ .../PlainTextSnapshotDiffReporter.java | 35 +++++ .../reporters/SnapshotDiffReporter.java | 11 ++ .../PlainTextEqualsComparatorTest.java | 31 ++++ .../PlainTextSnapshotDiffReporterTest.java | 33 +++++ ...ministicJacksonSnapshotSerializerTest.java | 2 +- .../JacksonSnapshotSerializerTest.java | 2 +- ...ministicJacksonSnapshotSerializerTest.snap | 4 +- .../JacksonSnapshotSerializerTest.snap | 4 +- 17 files changed, 349 insertions(+), 66 deletions(-) create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java create mode 100644 java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java create mode 100644 java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java diff --git a/build.gradle b/build.gradle index 7f6f8b94..ea60e3a3 100644 --- a/build.gradle +++ b/build.gradle @@ -28,10 +28,10 @@ subprojects { subproject -> dependencies { // Lombok - compileOnly 'org.projectlombok:lombok:1.18.8' - annotationProcessor 'org.projectlombok:lombok:1.18.8' - testCompileOnly 'org.projectlombok:lombok:1.18.8' - testAnnotationProcessor 'org.projectlombok:lombok:1.18.8' + compileOnly 'org.projectlombok:lombok:1.18.16' + annotationProcessor 'org.projectlombok:lombok:1.18.16' + testCompileOnly 'org.projectlombok:lombok:1.18.16' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.16' } // Adds verbose logging for TravisCI @@ -41,4 +41,4 @@ subprojects { subproject -> exceptionFormat "full" } } -} \ No newline at end of file +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java index 470b402b..cff9d0d6 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java @@ -1,43 +1,60 @@ package au.com.origin.snapshots; +import au.com.origin.snapshots.comparators.CompareResult; +import au.com.origin.snapshots.comparators.SnapshotComparator; import au.com.origin.snapshots.exceptions.SnapshotMatchException; +import au.com.origin.snapshots.reporters.SnapshotDiffReporter; import au.com.origin.snapshots.serializers.DeterministicJacksonSnapshotSerializer; import au.com.origin.snapshots.serializers.JacksonSnapshotSerializer; import au.com.origin.snapshots.serializers.SnapshotSerializer; import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.SneakyThrows; +import lombok.With; import org.apache.commons.lang3.StringUtils; -import org.assertj.core.util.diff.DiffUtils; -import org.assertj.core.util.diff.Patch; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class Snapshot { - private SnapshotConfig snapshotConfig; - private SnapshotSerializer snapshotSerializer; + private final SnapshotConfig snapshotConfig; private final SnapshotFile snapshotFile; - private final Class testClass; + private final Class testClass; private final Method testMethod; private final Object[] current; - private String scenario = null; + @With + private final SnapshotSerializer snapshotSerializer; + @With + private final SnapshotComparator snapshotComparator; + @With + private final List snapshotDiffReporters; + @With + private final String scenario; Snapshot( SnapshotConfig snapshotConfig, SnapshotFile snapshotFile, - Class testClass, + Class testClass, Method testMethod, Object... current) { - this.snapshotConfig = snapshotConfig; - this.snapshotSerializer = snapshotConfig.getSerializer(); - this.current = current; - this.snapshotFile = snapshotFile; - this.testClass = testClass; - this.testMethod = testMethod; + this(snapshotConfig, + snapshotFile, + testClass, + testMethod, + current, + snapshotConfig.getSerializer(), + snapshotConfig.getComparator(), + snapshotConfig.getDiffReporters(), + null); } /** @@ -50,8 +67,7 @@ public class Snapshot { * @return Snapshot */ public Snapshot scenario(String scenario) { - this.scenario = scenario; - return this; + return this.withScenario(scenario); } /** @@ -61,8 +77,51 @@ public Snapshot scenario(String scenario) { * @return Snapshot */ public Snapshot serializer(SnapshotSerializer serializer) { - this.snapshotSerializer = serializer; - return this; + return this.withSnapshotSerializer(serializer); + } + + /** + * Apply a custom comparator for this snapshot + * + * @param comparator your custom comparator + * @return Snapshot + */ + public Snapshot comparator(SnapshotComparator comparator) { + return this.withSnapshotComparator(comparator); + } + + /** + * Apply a list of custom diff reporters for this snapshot + * This will replace the default reporters defined in the config + * + * @param diffReporters your custom diff reporters + * @return Snapshot + */ + public Snapshot reporters(List diffReporters) { + return this.withSnapshotDiffReporters(diffReporters); + } + + /** + * Apply a single custom diff reporter for this snapshot + * This will replace the default reporters defined in the config + * + * @param diffReporter your custom diff reporter + * @return Snapshot + */ + public Snapshot reporter(SnapshotDiffReporter diffReporter) { + return this.withSnapshotDiffReporters(Collections.singletonList(diffReporter)); + } + + /** + * Add a single custom diff reporter to this snapshot + * + * @param diffReporter your custom diff reporter + * @return Snapshot + */ + public Snapshot extraReporter(SnapshotDiffReporter diffReporter) { + Stream supplied = Stream.of(diffReporter); + Stream existing = snapshotDiffReporters.stream(); + return this.withSnapshotDiffReporters(Stream.concat(existing, supplied).collect(Collectors.toList())); } /** @@ -93,12 +152,11 @@ public Snapshot orderedJson() { * Apply a custom serializer for this snapshot * * @param serializer your custom serializer - * @return this + * @return this */ @SneakyThrows public Snapshot serializer(Class serializer) { - this.snapshotSerializer = serializer.newInstance(); - return this; + return this.withSnapshotSerializer(serializer.getConstructor().newInstance()); } public void toMatchSnapshot() { @@ -116,9 +174,25 @@ public void toMatchSnapshot() { if (rawSnapshot != null) { // Match existing Snapshot - if (!rawSnapshot.trim().equals(currentObject.trim())) { + CompareResult compareResult = snapshotComparator.compare(getSnapshotName(), rawSnapshot, currentObject); + if (!compareResult.isSnapshotsMatch()) { snapshotFile.createDebugFile(currentObject.trim()); - throw generateDiffError(rawSnapshot, currentObject); + + List reporters = snapshotDiffReporters + .stream() + .filter(reporter -> reporter.supportsComparator(snapshotComparator)) + .collect(Collectors.toList()); + + if (reporters.isEmpty()) { + String comparator = snapshotComparator.getClass().getSimpleName(); + throw new IllegalStateException("No compatible reporters found for comparator " + comparator); + } + + for (SnapshotDiffReporter reporter : reporters) { + reporter.reportDiff(compareResult, currentObject); + } + + throw new SnapshotMatchException("Error matching snapshot"); } } else { // Create New Snapshot @@ -135,28 +209,8 @@ private boolean shouldUpdateSnapshot() { } } - private SnapshotMatchException generateDiffError(String rawSnapshot, String currentObject) { - // compute the patch: this is the diffutils part - Patch patch = - DiffUtils.diff( - Arrays.asList(rawSnapshot.trim().split("\n")), - Arrays.asList(currentObject.trim().split("\n"))); - String error = - "Error on: \n" - + currentObject.trim() - + "\n\n" - + patch - .getDeltas() - .stream() - .map(delta -> delta.toString() + "\n") - .reduce(String::concat) - .get(); - return new SnapshotMatchException(error); - } - private String getRawSnapshot(Collection rawSnapshots) { for (String rawSnapshot : rawSnapshots) { - if (rawSnapshot.contains(getSnapshotName())) { return rawSnapshot; } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java index a96881ea..0142d616 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java @@ -1,18 +1,24 @@ package au.com.origin.snapshots; +import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; +import au.com.origin.snapshots.comparators.SnapshotComparator; +import au.com.origin.snapshots.reporters.PlainTextSnapshotDiffReporter; +import au.com.origin.snapshots.reporters.SnapshotDiffReporter; import au.com.origin.snapshots.serializers.SnapshotSerializer; import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; import java.util.Optional; /** - * Snapshot Configuration - * ---------------------- + * Snapshot Configuration + * ---------------------- * - * Implement this interface when integrating `java-snapshot-testing` with a custom testing library + * Implement this interface when integrating `java-snapshot-testing` with a custom testing library * - * For frameworks - consider extending the framework implementation instead + * For frameworks - consider extending the framework implementation instead */ public interface SnapshotConfig { String JVM_UPDATE_SNAPSHOTS_PARAMETER = "updateSnapshot"; @@ -67,7 +73,7 @@ default String getSnapshotFolder() { * However, some don't and can use this method to discover it. * * @param testClass class under test - * @return method under tests + * @return method under tests */ Method getTestMethod(Class testClass); @@ -77,12 +83,7 @@ default String getSnapshotFolder() { * @return snapshots should be updated automatically without verification */ default Optional updateSnapshot() { - String value = System.getProperty(JVM_UPDATE_SNAPSHOTS_PARAMETER); - if ( value != null) { - return Optional.of(value); - } else { - return Optional.empty(); - } + return Optional.ofNullable(System.getProperty(JVM_UPDATE_SNAPSHOTS_PARAMETER)); } /** @@ -95,4 +96,31 @@ default SnapshotSerializer getSerializer() { return new ToStringSnapshotSerializer(); } + /** + * Optional + * Override to supply your own custom comparator function + * + * @return custom comparator function + */ + default SnapshotComparator getComparator() { + return new PlainTextEqualsComparator(); + } + + /** + * Optional + * Override to supply your own custom diff reporter functions + * Reporters will run in the same sequence as provided. + * Reporters can choose to throw exceptions in which case subsequent reporters will be skipped. + * Typically one such reporter can be included at the end which can use common assertion libraries + * like assertj or junit assertions to 'fail' the test. This is useful since IDEs like intellij can + * then show a diff using their native diff tools. + *

+ * In the absence of any 'throwing' reporters in the list, a default failure exception will be thrown + * to make sure tests fail. + * + * @return custom reporter functions + */ + default List getDiffReporters() { + return Collections.singletonList(new PlainTextSnapshotDiffReporter()); + } } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java index 2bddf983..71efd3a5 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java @@ -22,7 +22,7 @@ @RequiredArgsConstructor public class SnapshotVerifier { - private final Class testClass; + private final Class testClass; private final SnapshotFile snapshotFile; private final SnapshotConfig config; private final boolean failOnOrphans; @@ -54,6 +54,7 @@ public void validateSnapshots() { for (String snapshotName : snapshotNames) { if (rawSnapshot.contains(snapshotName)) { foundSnapshot = true; + break; } } if (!foundSnapshot) { diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java new file mode 100644 index 00000000..8436e35e --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java @@ -0,0 +1,16 @@ +package au.com.origin.snapshots.comparators; + +import lombok.Value; + +import java.util.Optional; + +@Value +public class CompareResult { + + private final boolean snapshotsMatch; + private final T comparisonResult; + + public Optional getComparisonResult() { + return Optional.ofNullable(this.comparisonResult); + } +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java new file mode 100644 index 00000000..0c63f616 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java @@ -0,0 +1,14 @@ +package au.com.origin.snapshots.comparators; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class CompareUtils { + public static CompareResult success() { + return new CompareResult(true, null); + } + + public static CompareResult failure(T result) { + return new CompareResult(false, result); + } +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java new file mode 100644 index 00000000..fa072cea --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java @@ -0,0 +1,23 @@ +package au.com.origin.snapshots.comparators; + +import org.assertj.core.util.diff.DiffUtils; +import org.assertj.core.util.diff.Patch; + +import java.util.Arrays; + +public class PlainTextEqualsComparator implements SnapshotComparator> { + + @Override + public CompareResult> compare(String snapshotName, String rawSnapshot, String currentObject) { + if (!rawSnapshot.trim().equals(currentObject.trim())) { + return CompareUtils.failure(getDiff(rawSnapshot, currentObject)); + } + return CompareUtils.success(); + } + + private static Patch getDiff(String rawSnapshot, String currentObject) { + return DiffUtils.diff( + Arrays.asList(rawSnapshot.trim().split("\n")), + Arrays.asList(currentObject.trim().split("\n"))); + } +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java new file mode 100644 index 00000000..1029aa72 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java @@ -0,0 +1,5 @@ +package au.com.origin.snapshots.comparators; + +public interface SnapshotComparator { + CompareResult compare(String snapshotName, String rawSnapshot, String currentObject); +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java new file mode 100644 index 00000000..59837c10 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java @@ -0,0 +1,32 @@ +package au.com.origin.snapshots.reporters; + +import au.com.origin.snapshots.comparators.CompareResult; +import au.com.origin.snapshots.comparators.SnapshotComparator; + +import java.util.List; +import java.util.function.Supplier; + +/* + * Adapter to make sure implementations of SnapshotDiffReporter are type-safe. + * Helps to avoid having to suppress the unchecked warning on every implementation. + */ +public abstract class AbstractComparatorAwareSnapshotDiffReporter implements SnapshotDiffReporter { + + private static final Supplier NO_RESULT_EXCEPTION_SUPPLIER = + () -> new IllegalStateException("No comparison result found for reporting"); + + @Override + public final boolean supportsComparator(SnapshotComparator given) { + return this.supportedComparators().stream().anyMatch(supported -> supported.isInstance(given)); + } + + @Override + @SuppressWarnings("unchecked") + public final void reportDiff(CompareResult compareResult, String currentObject) { + this.doReport((T) compareResult.getComparisonResult().orElseThrow(NO_RESULT_EXCEPTION_SUPPLIER), currentObject); + } + + protected abstract List>> supportedComparators(); + + protected abstract void doReport(T result, String currentObject); +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java new file mode 100644 index 00000000..a2e4d531 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java @@ -0,0 +1,35 @@ +package au.com.origin.snapshots.reporters; + +import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; +import au.com.origin.snapshots.comparators.SnapshotComparator; +import au.com.origin.snapshots.exceptions.SnapshotMatchException; +import org.assertj.core.util.diff.Patch; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +public class PlainTextSnapshotDiffReporter extends AbstractComparatorAwareSnapshotDiffReporter> { + + private static final Supplier NO_DIFF_EXCEPTION_SUPPLIER = + () -> new IllegalStateException("No differences found. Potential mismatch between comparator and reporter"); + + @Override + protected List>>> supportedComparators() { + return Collections.singletonList(PlainTextEqualsComparator.class); + } + + @Override + protected void doReport(Patch patch, String currentObject) { + throw new SnapshotMatchException("Error on: \n" + currentObject.trim() + "\n\n" + getDiffString(patch)); + } + + public static String getDiffString(Patch patch) { + return patch + .getDeltas() + .stream() + .map(delta -> delta.toString() + "\n") + .reduce(String::concat) + .orElseThrow(NO_DIFF_EXCEPTION_SUPPLIER); + } +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java new file mode 100644 index 00000000..48c4e4a2 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java @@ -0,0 +1,11 @@ +package au.com.origin.snapshots.reporters; + +import au.com.origin.snapshots.comparators.CompareResult; +import au.com.origin.snapshots.comparators.SnapshotComparator; + +public interface SnapshotDiffReporter { + + boolean supportsComparator(SnapshotComparator comparator); + + void reportDiff(CompareResult compareResult, String currentObject); +} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java new file mode 100644 index 00000000..ec5703c2 --- /dev/null +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java @@ -0,0 +1,31 @@ +package au.com.origin.snapshots.comparators; + +import au.com.origin.snapshots.reporters.PlainTextSnapshotDiffReporter; +import org.assertj.core.api.Assertions; +import org.assertj.core.util.diff.Patch; +import org.junit.jupiter.api.Test; + +class PlainTextEqualsComparatorTest { + + private static final PlainTextEqualsComparator COMPARATOR = new PlainTextEqualsComparator(); + + @Test + void successfulComparison() { + CompareResult> result = COMPARATOR.compare("snap1", "blah", "blah"); + + Assertions.assertThat(result).isEqualTo(CompareUtils.success()); + } + + @Test + void failingComparison() { + CompareResult> result = COMPARATOR.compare("snap1", "blah", "blahblah"); + + Assertions.assertThat(result.isSnapshotsMatch()).isFalse(); + Assertions.assertThat(result.getComparisonResult()).isNotEmpty(); + + String diffString = PlainTextSnapshotDiffReporter.getDiffString(result.getComparisonResult().get()); + String trimmed = diffString.replace("\n", " ").replaceAll(" +", " "); + + Assertions.assertThat(trimmed).contains("expecting: [\"blah\"] but was: [\"blahblah\"]"); + } +} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java new file mode 100644 index 00000000..ff2f3f2f --- /dev/null +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java @@ -0,0 +1,33 @@ +package au.com.origin.snapshots.reporters; + +import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; +import au.com.origin.snapshots.exceptions.SnapshotMatchException; +import org.assertj.core.api.Assertions; +import org.assertj.core.util.diff.DiffUtils; +import org.assertj.core.util.diff.Patch; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +class PlainTextSnapshotDiffReporterTest { + private static final PlainTextSnapshotDiffReporter REPORTER = new PlainTextSnapshotDiffReporter(); + + @Test + void supportedComparators() { + Assertions.assertThat(REPORTER.supportedComparators()).contains(PlainTextEqualsComparator.class); + } + + @Test + void doReport() { + Patch patch = DiffUtils.diff(Collections.singletonList("blah"), Collections.singletonList("bloo")); + + assertThatExceptionOfType(SnapshotMatchException.class) + .isThrownBy(() -> REPORTER.doReport(patch, "bloo")) + .withMessageContaining("expecting:") + .withMessageContaining("[\"blah\"]") + .withMessageContaining("but was:") + .withMessageContaining("[\"bloo\"]"); + } +} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java index efc1508c..337ada0f 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java @@ -47,7 +47,7 @@ private final class TypeDummy { private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); private final LocalDate localDate = LocalDate.parse("2020-10-19"); private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); - private final ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-10-19T22:21:07.103+10:00[Australia/Melbourne]"); + private final ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); private final AnEnum anEnum = AnEnum.A; private final Optional presentOptional = Optional.of("Hello World"); private final Optional emptyOptional = Optional.empty(); diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java index 64fc56d3..702c37ec 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java @@ -47,7 +47,7 @@ private final class TypeDummy { private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); private final LocalDate localDate = LocalDate.parse("2020-10-19"); private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); - private final ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-10-19T22:21:07.103+10:00[Australia/Melbourne]"); + private final ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); private final AnEnum anEnum = AnEnum.A; private final Optional presentOptional = Optional.of("Hello World"); private final Optional emptyOptional = Optional.empty(); diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap index f272f312..3eb24a10 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap @@ -135,6 +135,6 @@ au.com.origin.snapshots.serializers.DeterministicJacksonSnapshotSerializerTest.s "f", "g" ], - "zonedDateTime": "2020-10-19T22:21:07.103+11:00[Australia/Melbourne]" + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" } -] \ No newline at end of file +] diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap index eb7be51d..74c96599 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap @@ -13,7 +13,7 @@ au.com.origin.snapshots.serializers.JacksonSnapshotSerializerTest.shouldSerializ "date": "2020-10-19T22:21:07.103+00:00", "localDate": "2020-10-19", "localDateTime": "2020-10-19T22:21:07.103", - "zonedDateTime": "2020-10-19T22:21:07.103+11:00[Australia/Melbourne]", + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]", "anEnum": "A", "presentOptional": "Hello World", "emptyOptional": null, @@ -137,4 +137,4 @@ au.com.origin.snapshots.serializers.JacksonSnapshotSerializerTest.shouldSerializ ] ] } -] \ No newline at end of file +] From 44fb2398a8d7e082e6285f0969da50563a85ac7a Mon Sep 17 00:00:00 2001 From: Juja Date: Thu, 12 Nov 2020 17:59:27 +0530 Subject: [PATCH 02/13] Initial work-in-progress commit to enable support for custom snapshot comparators and reporters --- build.gradle | 10 +- .../au/com/origin/snapshots/Snapshot.java | 140 ++++++++++++------ .../com/origin/snapshots/SnapshotConfig.java | 43 +++++- .../origin/snapshots/SnapshotVerifier.java | 3 +- .../snapshots/comparators/CompareResult.java | 16 ++ .../snapshots/comparators/CompareUtils.java | 14 ++ .../PlainTextEqualsComparator.java | 23 +++ .../comparators/SnapshotComparator.java | 5 + ...ctComparatorAwareSnapshotDiffReporter.java | 32 ++++ .../PlainTextSnapshotDiffReporter.java | 35 +++++ .../reporters/SnapshotDiffReporter.java | 11 ++ .../PlainTextEqualsComparatorTest.java | 31 ++++ .../PlainTextSnapshotDiffReporterTest.java | 33 +++++ ...ministicJacksonSnapshotSerializerTest.java | 2 +- .../JacksonSnapshotSerializerTest.java | 2 +- ...ministicJacksonSnapshotSerializerTest.snap | 4 +- .../JacksonSnapshotSerializerTest.snap | 4 +- 17 files changed, 346 insertions(+), 62 deletions(-) create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java create mode 100644 java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java create mode 100644 java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java diff --git a/build.gradle b/build.gradle index 7f6f8b94..ea60e3a3 100644 --- a/build.gradle +++ b/build.gradle @@ -28,10 +28,10 @@ subprojects { subproject -> dependencies { // Lombok - compileOnly 'org.projectlombok:lombok:1.18.8' - annotationProcessor 'org.projectlombok:lombok:1.18.8' - testCompileOnly 'org.projectlombok:lombok:1.18.8' - testAnnotationProcessor 'org.projectlombok:lombok:1.18.8' + compileOnly 'org.projectlombok:lombok:1.18.16' + annotationProcessor 'org.projectlombok:lombok:1.18.16' + testCompileOnly 'org.projectlombok:lombok:1.18.16' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.16' } // Adds verbose logging for TravisCI @@ -41,4 +41,4 @@ subprojects { subproject -> exceptionFormat "full" } } -} \ No newline at end of file +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java index 470b402b..cff9d0d6 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java @@ -1,43 +1,60 @@ package au.com.origin.snapshots; +import au.com.origin.snapshots.comparators.CompareResult; +import au.com.origin.snapshots.comparators.SnapshotComparator; import au.com.origin.snapshots.exceptions.SnapshotMatchException; +import au.com.origin.snapshots.reporters.SnapshotDiffReporter; import au.com.origin.snapshots.serializers.DeterministicJacksonSnapshotSerializer; import au.com.origin.snapshots.serializers.JacksonSnapshotSerializer; import au.com.origin.snapshots.serializers.SnapshotSerializer; import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.SneakyThrows; +import lombok.With; import org.apache.commons.lang3.StringUtils; -import org.assertj.core.util.diff.DiffUtils; -import org.assertj.core.util.diff.Patch; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class Snapshot { - private SnapshotConfig snapshotConfig; - private SnapshotSerializer snapshotSerializer; + private final SnapshotConfig snapshotConfig; private final SnapshotFile snapshotFile; - private final Class testClass; + private final Class testClass; private final Method testMethod; private final Object[] current; - private String scenario = null; + @With + private final SnapshotSerializer snapshotSerializer; + @With + private final SnapshotComparator snapshotComparator; + @With + private final List snapshotDiffReporters; + @With + private final String scenario; Snapshot( SnapshotConfig snapshotConfig, SnapshotFile snapshotFile, - Class testClass, + Class testClass, Method testMethod, Object... current) { - this.snapshotConfig = snapshotConfig; - this.snapshotSerializer = snapshotConfig.getSerializer(); - this.current = current; - this.snapshotFile = snapshotFile; - this.testClass = testClass; - this.testMethod = testMethod; + this(snapshotConfig, + snapshotFile, + testClass, + testMethod, + current, + snapshotConfig.getSerializer(), + snapshotConfig.getComparator(), + snapshotConfig.getDiffReporters(), + null); } /** @@ -50,8 +67,7 @@ public class Snapshot { * @return Snapshot */ public Snapshot scenario(String scenario) { - this.scenario = scenario; - return this; + return this.withScenario(scenario); } /** @@ -61,8 +77,51 @@ public Snapshot scenario(String scenario) { * @return Snapshot */ public Snapshot serializer(SnapshotSerializer serializer) { - this.snapshotSerializer = serializer; - return this; + return this.withSnapshotSerializer(serializer); + } + + /** + * Apply a custom comparator for this snapshot + * + * @param comparator your custom comparator + * @return Snapshot + */ + public Snapshot comparator(SnapshotComparator comparator) { + return this.withSnapshotComparator(comparator); + } + + /** + * Apply a list of custom diff reporters for this snapshot + * This will replace the default reporters defined in the config + * + * @param diffReporters your custom diff reporters + * @return Snapshot + */ + public Snapshot reporters(List diffReporters) { + return this.withSnapshotDiffReporters(diffReporters); + } + + /** + * Apply a single custom diff reporter for this snapshot + * This will replace the default reporters defined in the config + * + * @param diffReporter your custom diff reporter + * @return Snapshot + */ + public Snapshot reporter(SnapshotDiffReporter diffReporter) { + return this.withSnapshotDiffReporters(Collections.singletonList(diffReporter)); + } + + /** + * Add a single custom diff reporter to this snapshot + * + * @param diffReporter your custom diff reporter + * @return Snapshot + */ + public Snapshot extraReporter(SnapshotDiffReporter diffReporter) { + Stream supplied = Stream.of(diffReporter); + Stream existing = snapshotDiffReporters.stream(); + return this.withSnapshotDiffReporters(Stream.concat(existing, supplied).collect(Collectors.toList())); } /** @@ -93,12 +152,11 @@ public Snapshot orderedJson() { * Apply a custom serializer for this snapshot * * @param serializer your custom serializer - * @return this + * @return this */ @SneakyThrows public Snapshot serializer(Class serializer) { - this.snapshotSerializer = serializer.newInstance(); - return this; + return this.withSnapshotSerializer(serializer.getConstructor().newInstance()); } public void toMatchSnapshot() { @@ -116,9 +174,25 @@ public void toMatchSnapshot() { if (rawSnapshot != null) { // Match existing Snapshot - if (!rawSnapshot.trim().equals(currentObject.trim())) { + CompareResult compareResult = snapshotComparator.compare(getSnapshotName(), rawSnapshot, currentObject); + if (!compareResult.isSnapshotsMatch()) { snapshotFile.createDebugFile(currentObject.trim()); - throw generateDiffError(rawSnapshot, currentObject); + + List reporters = snapshotDiffReporters + .stream() + .filter(reporter -> reporter.supportsComparator(snapshotComparator)) + .collect(Collectors.toList()); + + if (reporters.isEmpty()) { + String comparator = snapshotComparator.getClass().getSimpleName(); + throw new IllegalStateException("No compatible reporters found for comparator " + comparator); + } + + for (SnapshotDiffReporter reporter : reporters) { + reporter.reportDiff(compareResult, currentObject); + } + + throw new SnapshotMatchException("Error matching snapshot"); } } else { // Create New Snapshot @@ -135,28 +209,8 @@ private boolean shouldUpdateSnapshot() { } } - private SnapshotMatchException generateDiffError(String rawSnapshot, String currentObject) { - // compute the patch: this is the diffutils part - Patch patch = - DiffUtils.diff( - Arrays.asList(rawSnapshot.trim().split("\n")), - Arrays.asList(currentObject.trim().split("\n"))); - String error = - "Error on: \n" - + currentObject.trim() - + "\n\n" - + patch - .getDeltas() - .stream() - .map(delta -> delta.toString() + "\n") - .reduce(String::concat) - .get(); - return new SnapshotMatchException(error); - } - private String getRawSnapshot(Collection rawSnapshots) { for (String rawSnapshot : rawSnapshots) { - if (rawSnapshot.contains(getSnapshotName())) { return rawSnapshot; } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java index 8bad21e4..364ce462 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java @@ -1,9 +1,15 @@ package au.com.origin.snapshots; +import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; +import au.com.origin.snapshots.comparators.SnapshotComparator; +import au.com.origin.snapshots.reporters.PlainTextSnapshotDiffReporter; +import au.com.origin.snapshots.reporters.SnapshotDiffReporter; import au.com.origin.snapshots.serializers.SnapshotSerializer; import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; import java.util.Optional; /** @@ -77,12 +83,7 @@ default String getSnapshotFolder() { * @return snapshots should be updated automatically without verification */ default Optional updateSnapshot() { - String value = System.getProperty(JVM_UPDATE_SNAPSHOTS_PARAMETER); - if ( value != null) { - return Optional.of(value); - } else { - return Optional.empty(); - } + return Optional.ofNullable(System.getProperty(JVM_UPDATE_SNAPSHOTS_PARAMETER)); } /** @@ -95,7 +96,6 @@ default SnapshotSerializer getSerializer() { return new ToStringSnapshotSerializer(); } - /** * Optional * Allows you to perform any custom modifications to the file before it is saved @@ -107,4 +107,33 @@ default SnapshotSerializer getSerializer() { default String onSaveSnapshotFile(Class testClass, String snapshotContent) { return snapshotContent; } + + + /** + * Optional + * Override to supply your own custom comparator function + * + * @return custom comparator function + */ + default SnapshotComparator getComparator() { + return new PlainTextEqualsComparator(); + } + + /** + * Optional + * Override to supply your own custom diff reporter functions + * Reporters will run in the same sequence as provided. + * Reporters can choose to throw exceptions in which case subsequent reporters will be skipped. + * Typically one such reporter can be included at the end which can use common assertion libraries + * like assertj or junit assertions to 'fail' the test. This is useful since IDEs like intellij can + * then show a diff using their native diff tools. + *

+ * In the absence of any 'throwing' reporters in the list, a default failure exception will be thrown + * to make sure tests fail. + * + * @return custom reporter functions + */ + default List getDiffReporters() { + return Collections.singletonList(new PlainTextSnapshotDiffReporter()); + } } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java index 8bf97e77..10606833 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotVerifier.java @@ -22,7 +22,7 @@ @RequiredArgsConstructor public class SnapshotVerifier { - private final Class testClass; + private final Class testClass; private final SnapshotFile snapshotFile; private final SnapshotConfig config; private final boolean failOnOrphans; @@ -54,6 +54,7 @@ public void validateSnapshots() { for (String snapshotName : snapshotNames) { if (rawSnapshot.contains(snapshotName)) { foundSnapshot = true; + break; } } if (!foundSnapshot) { diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java new file mode 100644 index 00000000..8436e35e --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java @@ -0,0 +1,16 @@ +package au.com.origin.snapshots.comparators; + +import lombok.Value; + +import java.util.Optional; + +@Value +public class CompareResult { + + private final boolean snapshotsMatch; + private final T comparisonResult; + + public Optional getComparisonResult() { + return Optional.ofNullable(this.comparisonResult); + } +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java new file mode 100644 index 00000000..0c63f616 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java @@ -0,0 +1,14 @@ +package au.com.origin.snapshots.comparators; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class CompareUtils { + public static CompareResult success() { + return new CompareResult(true, null); + } + + public static CompareResult failure(T result) { + return new CompareResult(false, result); + } +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java new file mode 100644 index 00000000..fa072cea --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java @@ -0,0 +1,23 @@ +package au.com.origin.snapshots.comparators; + +import org.assertj.core.util.diff.DiffUtils; +import org.assertj.core.util.diff.Patch; + +import java.util.Arrays; + +public class PlainTextEqualsComparator implements SnapshotComparator> { + + @Override + public CompareResult> compare(String snapshotName, String rawSnapshot, String currentObject) { + if (!rawSnapshot.trim().equals(currentObject.trim())) { + return CompareUtils.failure(getDiff(rawSnapshot, currentObject)); + } + return CompareUtils.success(); + } + + private static Patch getDiff(String rawSnapshot, String currentObject) { + return DiffUtils.diff( + Arrays.asList(rawSnapshot.trim().split("\n")), + Arrays.asList(currentObject.trim().split("\n"))); + } +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java new file mode 100644 index 00000000..1029aa72 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java @@ -0,0 +1,5 @@ +package au.com.origin.snapshots.comparators; + +public interface SnapshotComparator { + CompareResult compare(String snapshotName, String rawSnapshot, String currentObject); +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java new file mode 100644 index 00000000..59837c10 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java @@ -0,0 +1,32 @@ +package au.com.origin.snapshots.reporters; + +import au.com.origin.snapshots.comparators.CompareResult; +import au.com.origin.snapshots.comparators.SnapshotComparator; + +import java.util.List; +import java.util.function.Supplier; + +/* + * Adapter to make sure implementations of SnapshotDiffReporter are type-safe. + * Helps to avoid having to suppress the unchecked warning on every implementation. + */ +public abstract class AbstractComparatorAwareSnapshotDiffReporter implements SnapshotDiffReporter { + + private static final Supplier NO_RESULT_EXCEPTION_SUPPLIER = + () -> new IllegalStateException("No comparison result found for reporting"); + + @Override + public final boolean supportsComparator(SnapshotComparator given) { + return this.supportedComparators().stream().anyMatch(supported -> supported.isInstance(given)); + } + + @Override + @SuppressWarnings("unchecked") + public final void reportDiff(CompareResult compareResult, String currentObject) { + this.doReport((T) compareResult.getComparisonResult().orElseThrow(NO_RESULT_EXCEPTION_SUPPLIER), currentObject); + } + + protected abstract List>> supportedComparators(); + + protected abstract void doReport(T result, String currentObject); +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java new file mode 100644 index 00000000..a2e4d531 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java @@ -0,0 +1,35 @@ +package au.com.origin.snapshots.reporters; + +import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; +import au.com.origin.snapshots.comparators.SnapshotComparator; +import au.com.origin.snapshots.exceptions.SnapshotMatchException; +import org.assertj.core.util.diff.Patch; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +public class PlainTextSnapshotDiffReporter extends AbstractComparatorAwareSnapshotDiffReporter> { + + private static final Supplier NO_DIFF_EXCEPTION_SUPPLIER = + () -> new IllegalStateException("No differences found. Potential mismatch between comparator and reporter"); + + @Override + protected List>>> supportedComparators() { + return Collections.singletonList(PlainTextEqualsComparator.class); + } + + @Override + protected void doReport(Patch patch, String currentObject) { + throw new SnapshotMatchException("Error on: \n" + currentObject.trim() + "\n\n" + getDiffString(patch)); + } + + public static String getDiffString(Patch patch) { + return patch + .getDeltas() + .stream() + .map(delta -> delta.toString() + "\n") + .reduce(String::concat) + .orElseThrow(NO_DIFF_EXCEPTION_SUPPLIER); + } +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java new file mode 100644 index 00000000..48c4e4a2 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java @@ -0,0 +1,11 @@ +package au.com.origin.snapshots.reporters; + +import au.com.origin.snapshots.comparators.CompareResult; +import au.com.origin.snapshots.comparators.SnapshotComparator; + +public interface SnapshotDiffReporter { + + boolean supportsComparator(SnapshotComparator comparator); + + void reportDiff(CompareResult compareResult, String currentObject); +} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java new file mode 100644 index 00000000..ec5703c2 --- /dev/null +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java @@ -0,0 +1,31 @@ +package au.com.origin.snapshots.comparators; + +import au.com.origin.snapshots.reporters.PlainTextSnapshotDiffReporter; +import org.assertj.core.api.Assertions; +import org.assertj.core.util.diff.Patch; +import org.junit.jupiter.api.Test; + +class PlainTextEqualsComparatorTest { + + private static final PlainTextEqualsComparator COMPARATOR = new PlainTextEqualsComparator(); + + @Test + void successfulComparison() { + CompareResult> result = COMPARATOR.compare("snap1", "blah", "blah"); + + Assertions.assertThat(result).isEqualTo(CompareUtils.success()); + } + + @Test + void failingComparison() { + CompareResult> result = COMPARATOR.compare("snap1", "blah", "blahblah"); + + Assertions.assertThat(result.isSnapshotsMatch()).isFalse(); + Assertions.assertThat(result.getComparisonResult()).isNotEmpty(); + + String diffString = PlainTextSnapshotDiffReporter.getDiffString(result.getComparisonResult().get()); + String trimmed = diffString.replace("\n", " ").replaceAll(" +", " "); + + Assertions.assertThat(trimmed).contains("expecting: [\"blah\"] but was: [\"blahblah\"]"); + } +} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java new file mode 100644 index 00000000..ff2f3f2f --- /dev/null +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java @@ -0,0 +1,33 @@ +package au.com.origin.snapshots.reporters; + +import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; +import au.com.origin.snapshots.exceptions.SnapshotMatchException; +import org.assertj.core.api.Assertions; +import org.assertj.core.util.diff.DiffUtils; +import org.assertj.core.util.diff.Patch; +import org.junit.jupiter.api.Test; + +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +class PlainTextSnapshotDiffReporterTest { + private static final PlainTextSnapshotDiffReporter REPORTER = new PlainTextSnapshotDiffReporter(); + + @Test + void supportedComparators() { + Assertions.assertThat(REPORTER.supportedComparators()).contains(PlainTextEqualsComparator.class); + } + + @Test + void doReport() { + Patch patch = DiffUtils.diff(Collections.singletonList("blah"), Collections.singletonList("bloo")); + + assertThatExceptionOfType(SnapshotMatchException.class) + .isThrownBy(() -> REPORTER.doReport(patch, "bloo")) + .withMessageContaining("expecting:") + .withMessageContaining("[\"blah\"]") + .withMessageContaining("but was:") + .withMessageContaining("[\"bloo\"]"); + } +} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java index efc1508c..337ada0f 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java @@ -47,7 +47,7 @@ private final class TypeDummy { private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); private final LocalDate localDate = LocalDate.parse("2020-10-19"); private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); - private final ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-10-19T22:21:07.103+10:00[Australia/Melbourne]"); + private final ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); private final AnEnum anEnum = AnEnum.A; private final Optional presentOptional = Optional.of("Hello World"); private final Optional emptyOptional = Optional.empty(); diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java index 64fc56d3..702c37ec 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java @@ -47,7 +47,7 @@ private final class TypeDummy { private final Date date = Date.from(Instant.parse("2020-10-19T22:21:07.103Z")); private final LocalDate localDate = LocalDate.parse("2020-10-19"); private final LocalDateTime localDateTime = LocalDateTime.parse("2020-10-19T22:21:07.103"); - private final ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-10-19T22:21:07.103+10:00[Australia/Melbourne]"); + private final ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-04-19T22:21:07.103+10:00[Australia/Melbourne]"); private final AnEnum anEnum = AnEnum.A; private final Optional presentOptional = Optional.of("Hello World"); private final Optional emptyOptional = Optional.empty(); diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap index f272f312..3eb24a10 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/DeterministicJacksonSnapshotSerializerTest.snap @@ -135,6 +135,6 @@ au.com.origin.snapshots.serializers.DeterministicJacksonSnapshotSerializerTest.s "f", "g" ], - "zonedDateTime": "2020-10-19T22:21:07.103+11:00[Australia/Melbourne]" + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]" } -] \ No newline at end of file +] diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap index eb7be51d..74c96599 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/__snapshots__/JacksonSnapshotSerializerTest.snap @@ -13,7 +13,7 @@ au.com.origin.snapshots.serializers.JacksonSnapshotSerializerTest.shouldSerializ "date": "2020-10-19T22:21:07.103+00:00", "localDate": "2020-10-19", "localDateTime": "2020-10-19T22:21:07.103", - "zonedDateTime": "2020-10-19T22:21:07.103+11:00[Australia/Melbourne]", + "zonedDateTime": "2020-04-19T22:21:07.103+10:00[Australia/Melbourne]", "anEnum": "A", "presentOptional": "Hello World", "emptyOptional": null, @@ -137,4 +137,4 @@ au.com.origin.snapshots.serializers.JacksonSnapshotSerializerTest.shouldSerializ ] ] } -] \ No newline at end of file +] From 3bd0e287e933a8cb14196da7ad0177277023207d Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 09:25:44 +0530 Subject: [PATCH 03/13] Addressed comments from initial review - decoupled comparator and reporter - changed reporters helper method on Snapshot.java to accept a varargs reporters --- .../au/com/origin/snapshots/Snapshot.java | 45 ++++--------------- .../com/origin/snapshots/SnapshotConfig.java | 2 +- .../snapshots/comparators/CompareResult.java | 16 ------- .../snapshots/comparators/CompareUtils.java | 14 ------ .../PlainTextEqualsComparator.java | 20 ++------- .../comparators/SnapshotComparator.java | 4 +- ...ctComparatorAwareSnapshotDiffReporter.java | 32 ------------- .../PlainTextSnapshotDiffReporter.java | 17 +++---- .../reporters/SnapshotDiffReporter.java | 7 +-- .../JacksonSnapshotSerializer.java | 7 +++ .../serializers/SnapshotSerializer.java | 1 + .../ToStringSnapshotSerializer.java | 8 ++++ .../PlainTextEqualsComparatorTest.java | 16 +------ .../config/ToStringSnapshotConfig.java | 13 +++++- .../PlainTextSnapshotDiffReporterTest.java | 19 ++++---- ...ministicJacksonSnapshotSerializerTest.java | 6 +++ .../JacksonSnapshotSerializerTest.java | 6 +++ .../LowercaseToStringSerializer.java | 5 +++ .../ToStringSnapshotSerializerTest.java | 6 +++ .../UppercaseToStringSerializer.java | 5 +++ 20 files changed, 92 insertions(+), 157 deletions(-) delete mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java delete mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java delete mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java index cff9d0d6..0356b5ab 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java @@ -1,6 +1,5 @@ package au.com.origin.snapshots; -import au.com.origin.snapshots.comparators.CompareResult; import au.com.origin.snapshots.comparators.SnapshotComparator; import au.com.origin.snapshots.exceptions.SnapshotMatchException; import au.com.origin.snapshots.reporters.SnapshotDiffReporter; @@ -15,12 +14,8 @@ import org.apache.commons.lang3.StringUtils; import java.lang.reflect.Method; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; -import java.util.stream.Stream; @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Snapshot { @@ -34,7 +29,7 @@ public class Snapshot { @With private final SnapshotSerializer snapshotSerializer; @With - private final SnapshotComparator snapshotComparator; + private final SnapshotComparator snapshotComparator; @With private final List snapshotDiffReporters; @With @@ -86,7 +81,7 @@ public Snapshot serializer(SnapshotSerializer serializer) { * @param comparator your custom comparator * @return Snapshot */ - public Snapshot comparator(SnapshotComparator comparator) { + public Snapshot comparator(SnapshotComparator comparator) { return this.withSnapshotComparator(comparator); } @@ -97,31 +92,8 @@ public Snapshot comparator(SnapshotComparator comparator) { * @param diffReporters your custom diff reporters * @return Snapshot */ - public Snapshot reporters(List diffReporters) { - return this.withSnapshotDiffReporters(diffReporters); - } - - /** - * Apply a single custom diff reporter for this snapshot - * This will replace the default reporters defined in the config - * - * @param diffReporter your custom diff reporter - * @return Snapshot - */ - public Snapshot reporter(SnapshotDiffReporter diffReporter) { - return this.withSnapshotDiffReporters(Collections.singletonList(diffReporter)); - } - - /** - * Add a single custom diff reporter to this snapshot - * - * @param diffReporter your custom diff reporter - * @return Snapshot - */ - public Snapshot extraReporter(SnapshotDiffReporter diffReporter) { - Stream supplied = Stream.of(diffReporter); - Stream existing = snapshotDiffReporters.stream(); - return this.withSnapshotDiffReporters(Stream.concat(existing, supplied).collect(Collectors.toList())); + public Snapshot reporters(SnapshotDiffReporter... diffReporters) { + return this.withSnapshotDiffReporters(Arrays.asList(diffReporters)); } /** @@ -174,13 +146,12 @@ public void toMatchSnapshot() { if (rawSnapshot != null) { // Match existing Snapshot - CompareResult compareResult = snapshotComparator.compare(getSnapshotName(), rawSnapshot, currentObject); - if (!compareResult.isSnapshotsMatch()) { + if (!snapshotComparator.match(getSnapshotName(), rawSnapshot, currentObject)) { snapshotFile.createDebugFile(currentObject.trim()); List reporters = snapshotDiffReporters .stream() - .filter(reporter -> reporter.supportsComparator(snapshotComparator)) + .filter(reporter -> reporter.supportsFormat(snapshotSerializer.getOutputFormat())) .collect(Collectors.toList()); if (reporters.isEmpty()) { @@ -189,7 +160,7 @@ public void toMatchSnapshot() { } for (SnapshotDiffReporter reporter : reporters) { - reporter.reportDiff(compareResult, currentObject); + reporter.reportDiff(getSnapshotName(), rawSnapshot, currentObject); } throw new SnapshotMatchException("Error matching snapshot"); diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java index 028bd31e..ac56b177 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java @@ -115,7 +115,7 @@ default String onSaveSnapshotFile(Class testClass, String snapshotContent) { * * @return custom comparator function */ - default SnapshotComparator getComparator() { + default SnapshotComparator getComparator() { return new PlainTextEqualsComparator(); } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java deleted file mode 100644 index 8436e35e..00000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareResult.java +++ /dev/null @@ -1,16 +0,0 @@ -package au.com.origin.snapshots.comparators; - -import lombok.Value; - -import java.util.Optional; - -@Value -public class CompareResult { - - private final boolean snapshotsMatch; - private final T comparisonResult; - - public Optional getComparisonResult() { - return Optional.ofNullable(this.comparisonResult); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java deleted file mode 100644 index 0c63f616..00000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/CompareUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -package au.com.origin.snapshots.comparators; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class CompareUtils { - public static CompareResult success() { - return new CompareResult(true, null); - } - - public static CompareResult failure(T result) { - return new CompareResult(false, result); - } -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java index fa072cea..d55be463 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java @@ -1,23 +1,9 @@ package au.com.origin.snapshots.comparators; -import org.assertj.core.util.diff.DiffUtils; -import org.assertj.core.util.diff.Patch; - -import java.util.Arrays; - -public class PlainTextEqualsComparator implements SnapshotComparator> { +public class PlainTextEqualsComparator implements SnapshotComparator { @Override - public CompareResult> compare(String snapshotName, String rawSnapshot, String currentObject) { - if (!rawSnapshot.trim().equals(currentObject.trim())) { - return CompareUtils.failure(getDiff(rawSnapshot, currentObject)); - } - return CompareUtils.success(); - } - - private static Patch getDiff(String rawSnapshot, String currentObject) { - return DiffUtils.diff( - Arrays.asList(rawSnapshot.trim().split("\n")), - Arrays.asList(currentObject.trim().split("\n"))); + public boolean match(String snapshotName, String rawSnapshot, String currentObject) { + return rawSnapshot.trim().equals(currentObject.trim()); } } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java index 1029aa72..912f0036 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java @@ -1,5 +1,5 @@ package au.com.origin.snapshots.comparators; -public interface SnapshotComparator { - CompareResult compare(String snapshotName, String rawSnapshot, String currentObject); +public interface SnapshotComparator { + boolean match(String snapshotName, String rawSnapshot, String currentObject); } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java deleted file mode 100644 index 59837c10..00000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/AbstractComparatorAwareSnapshotDiffReporter.java +++ /dev/null @@ -1,32 +0,0 @@ -package au.com.origin.snapshots.reporters; - -import au.com.origin.snapshots.comparators.CompareResult; -import au.com.origin.snapshots.comparators.SnapshotComparator; - -import java.util.List; -import java.util.function.Supplier; - -/* - * Adapter to make sure implementations of SnapshotDiffReporter are type-safe. - * Helps to avoid having to suppress the unchecked warning on every implementation. - */ -public abstract class AbstractComparatorAwareSnapshotDiffReporter implements SnapshotDiffReporter { - - private static final Supplier NO_RESULT_EXCEPTION_SUPPLIER = - () -> new IllegalStateException("No comparison result found for reporting"); - - @Override - public final boolean supportsComparator(SnapshotComparator given) { - return this.supportedComparators().stream().anyMatch(supported -> supported.isInstance(given)); - } - - @Override - @SuppressWarnings("unchecked") - public final void reportDiff(CompareResult compareResult, String currentObject) { - this.doReport((T) compareResult.getComparisonResult().orElseThrow(NO_RESULT_EXCEPTION_SUPPLIER), currentObject); - } - - protected abstract List>> supportedComparators(); - - protected abstract void doReport(T result, String currentObject); -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java index a2e4d531..21b01528 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java @@ -1,26 +1,27 @@ package au.com.origin.snapshots.reporters; -import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; -import au.com.origin.snapshots.comparators.SnapshotComparator; import au.com.origin.snapshots.exceptions.SnapshotMatchException; +import org.assertj.core.util.diff.DiffUtils; import org.assertj.core.util.diff.Patch; -import java.util.Collections; -import java.util.List; +import java.util.Arrays; import java.util.function.Supplier; -public class PlainTextSnapshotDiffReporter extends AbstractComparatorAwareSnapshotDiffReporter> { +public class PlainTextSnapshotDiffReporter implements SnapshotDiffReporter { private static final Supplier NO_DIFF_EXCEPTION_SUPPLIER = () -> new IllegalStateException("No differences found. Potential mismatch between comparator and reporter"); @Override - protected List>>> supportedComparators() { - return Collections.singletonList(PlainTextEqualsComparator.class); + public boolean supportsFormat(String outputFormat) { + return true; // always true } @Override - protected void doReport(Patch patch, String currentObject) { + public void reportDiff(String snapshotName, String rawSnapshot, String currentObject) { + Patch patch = DiffUtils.diff( + Arrays.asList(rawSnapshot.trim().split("\n")), + Arrays.asList(currentObject.trim().split("\n"))); throw new SnapshotMatchException("Error on: \n" + currentObject.trim() + "\n\n" + getDiffString(patch)); } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java index 48c4e4a2..91f5087d 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java @@ -1,11 +1,8 @@ package au.com.origin.snapshots.reporters; -import au.com.origin.snapshots.comparators.CompareResult; -import au.com.origin.snapshots.comparators.SnapshotComparator; - public interface SnapshotDiffReporter { - boolean supportsComparator(SnapshotComparator comparator); + boolean supportsFormat(String outputFormat); - void reportDiff(CompareResult compareResult, String currentObject); + void reportDiff(String snapshotName, String rawSnapshot, String currentObject); } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializer.java index 1914092b..73924a97 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializer.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializer.java @@ -14,6 +14,8 @@ public class JacksonSnapshotSerializer implements SnapshotSerializer { + public static final String FORMAT = "json"; + /** * Override to customize the Jackson objectMapper * @param objectMapper existing ObjectMapper @@ -69,4 +71,9 @@ public String apply(Object[] objects) { throw new SnapshotExtensionException("Jackson Serialization failed", e); } } + + @Override + public String getOutputFormat() { + return FORMAT; + } } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SnapshotSerializer.java index 4282faef..b54baae2 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SnapshotSerializer.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SnapshotSerializer.java @@ -4,4 +4,5 @@ public interface SnapshotSerializer extends Function { + String getOutputFormat(); } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java index 0d59182b..9c866963 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java @@ -9,8 +9,16 @@ * Will render each toString() on a separate line */ public class ToStringSnapshotSerializer implements SnapshotSerializer { + + public static final String FORMAT = "string"; + @Override public String apply(Object[] objects) { return "[\n" + Arrays.stream(objects).map(Object::toString).collect(Collectors.joining("\n")) + "\n]"; } + + @Override + public String getOutputFormat() { + return FORMAT; + } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java index ec5703c2..79a3c8fc 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java @@ -1,8 +1,6 @@ package au.com.origin.snapshots.comparators; -import au.com.origin.snapshots.reporters.PlainTextSnapshotDiffReporter; import org.assertj.core.api.Assertions; -import org.assertj.core.util.diff.Patch; import org.junit.jupiter.api.Test; class PlainTextEqualsComparatorTest { @@ -11,21 +9,11 @@ class PlainTextEqualsComparatorTest { @Test void successfulComparison() { - CompareResult> result = COMPARATOR.compare("snap1", "blah", "blah"); - - Assertions.assertThat(result).isEqualTo(CompareUtils.success()); + Assertions.assertThat(COMPARATOR.match("snap1", "blah", "blah")).isTrue(); } @Test void failingComparison() { - CompareResult> result = COMPARATOR.compare("snap1", "blah", "blahblah"); - - Assertions.assertThat(result.isSnapshotsMatch()).isFalse(); - Assertions.assertThat(result.getComparisonResult()).isNotEmpty(); - - String diffString = PlainTextSnapshotDiffReporter.getDiffString(result.getComparisonResult().get()); - String trimmed = diffString.replace("\n", " ").replaceAll(" +", " "); - - Assertions.assertThat(trimmed).contains("expecting: [\"blah\"] but was: [\"blahblah\"]"); + Assertions.assertThat(COMPARATOR.match("snap1", "blah", "blahblah")).isFalse(); } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java index 616ecd4c..2c16467b 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java @@ -1,6 +1,7 @@ package au.com.origin.snapshots.config; import au.com.origin.snapshots.serializers.SnapshotSerializer; +import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; import java.util.Arrays; import java.util.stream.Collectors; @@ -9,6 +10,16 @@ public class ToStringSnapshotConfig extends BaseSnapshotConfig { @Override public SnapshotSerializer getSerializer() { - return objects -> Arrays.stream(objects).map(Object::toString).collect(Collectors.joining()); + return new SnapshotSerializer() { + @Override + public String getOutputFormat() { + return ToStringSnapshotSerializer.FORMAT; + } + + @Override + public String apply(Object[] objects) { + return Arrays.stream(objects).map(Object::toString).collect(Collectors.joining()); + } + }; } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java index ff2f3f2f..4809dff7 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java @@ -1,30 +1,29 @@ package au.com.origin.snapshots.reporters; -import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; import au.com.origin.snapshots.exceptions.SnapshotMatchException; +import au.com.origin.snapshots.serializers.JacksonSnapshotSerializer; +import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; import org.assertj.core.api.Assertions; -import org.assertj.core.util.diff.DiffUtils; -import org.assertj.core.util.diff.Patch; import org.junit.jupiter.api.Test; -import java.util.Collections; - import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class PlainTextSnapshotDiffReporterTest { private static final PlainTextSnapshotDiffReporter REPORTER = new PlainTextSnapshotDiffReporter(); @Test - void supportedComparators() { - Assertions.assertThat(REPORTER.supportedComparators()).contains(PlainTextEqualsComparator.class); + void shouldSupportAllFormats() { + Assertions.assertThat(REPORTER.supportsFormat(ToStringSnapshotSerializer.FORMAT)).isTrue(); + Assertions.assertThat(REPORTER.supportsFormat(JacksonSnapshotSerializer.FORMAT)).isTrue(); + + Assertions.assertThat(REPORTER.supportsFormat("xml")).isTrue(); + Assertions.assertThat(REPORTER.supportsFormat("blah")).isTrue(); } @Test void doReport() { - Patch patch = DiffUtils.diff(Collections.singletonList("blah"), Collections.singletonList("bloo")); - assertThatExceptionOfType(SnapshotMatchException.class) - .isThrownBy(() -> REPORTER.doReport(patch, "bloo")) + .isThrownBy(() -> REPORTER.reportDiff("snap1", "blah", "bloo")) .withMessageContaining("expecting:") .withMessageContaining("[\"blah\"]") .withMessageContaining("but was:") diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java index 337ada0f..0a38c54f 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java @@ -2,6 +2,7 @@ import au.com.origin.snapshots.SnapshotConfig; import au.com.origin.snapshots.config.BaseSnapshotConfig; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import java.time.Instant; @@ -28,6 +29,11 @@ public void shouldSerializeDifferentTypes() { validateSnapshots(); } + @Test + void shouldSupportJsonFormat() { + Assertions.assertThat(JacksonSnapshotSerializer.FORMAT).isEqualTo("json"); + } + private enum AnEnum { F, A, D, E, G, B, C; } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java index 702c37ec..2af2cca1 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java @@ -2,6 +2,7 @@ import au.com.origin.snapshots.SnapshotConfig; import au.com.origin.snapshots.config.BaseSnapshotConfig; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import java.time.Instant; @@ -28,6 +29,11 @@ public void shouldSerializeDifferentTypes() { validateSnapshots(); } + @Test + void shouldSupportJsonFormat() { + Assertions.assertThat(JacksonSnapshotSerializer.FORMAT).isEqualTo("json"); + } + private enum AnEnum { F, A, D, E, G, B, C; } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java index 911dee44..ecfb8962 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java @@ -8,4 +8,9 @@ public class LowercaseToStringSerializer implements SnapshotSerializer { public String apply(Object[] objects) { return Arrays.stream(objects).map(Object::toString).collect(Collectors.joining()).toLowerCase(); } + + @Override + public String getOutputFormat() { + return ToStringSnapshotSerializer.FORMAT; + } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java index ac6d2c29..c3ecd3b2 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java @@ -5,6 +5,7 @@ import au.com.origin.snapshots.config.BaseSnapshotConfig; import lombok.AllArgsConstructor; import lombok.Data; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -33,6 +34,11 @@ void canUseSnapshotConfigAnnotationAtClassLevel() { validateSnapshots(); } + @Test + void shouldSupportStringFormat() { + Assertions.assertThat(ToStringSnapshotSerializer.FORMAT).isEqualTo("string"); + } + @AllArgsConstructor @Data private static class Dummy { diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java index d95cb82a..97078775 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java @@ -8,4 +8,9 @@ public class UppercaseToStringSerializer implements SnapshotSerializer { public String apply(Object[] objects) { return Arrays.stream(objects).map(Object::toString).collect(Collectors.joining()).toUpperCase(); } + + @Override + public String getOutputFormat() { + return ToStringSnapshotSerializer.FORMAT; + } } From 6eb801aa09604e53e94e217b647871476fa5c2be Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 10:23:44 +0530 Subject: [PATCH 04/13] Introduce SerializerType enum --- .../snapshots/serializers/JacksonSnapshotSerializer.java | 4 +--- .../com/origin/snapshots/serializers/SerializerType.java | 5 +++++ .../snapshots/serializers/ToStringSnapshotSerializer.java | 4 +--- .../origin/snapshots/config/ToStringSnapshotConfig.java | 4 ++-- .../reporters/PlainTextSnapshotDiffReporterTest.java | 7 +++---- .../DeterministicJacksonSnapshotSerializerTest.java | 2 +- .../serializers/JacksonSnapshotSerializerTest.java | 2 +- .../snapshots/serializers/LowercaseToStringSerializer.java | 2 +- .../serializers/ToStringSnapshotSerializerTest.java | 2 +- .../snapshots/serializers/UppercaseToStringSerializer.java | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SerializerType.java diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializer.java index 73924a97..34f9aa9b 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializer.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializer.java @@ -14,8 +14,6 @@ public class JacksonSnapshotSerializer implements SnapshotSerializer { - public static final String FORMAT = "json"; - /** * Override to customize the Jackson objectMapper * @param objectMapper existing ObjectMapper @@ -74,6 +72,6 @@ public String apply(Object[] objects) { @Override public String getOutputFormat() { - return FORMAT; + return SerializerType.JSON.name(); } } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SerializerType.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SerializerType.java new file mode 100644 index 00000000..e75cc38d --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/SerializerType.java @@ -0,0 +1,5 @@ +package au.com.origin.snapshots.serializers; + +public enum SerializerType { + TEXT, JSON; +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java index 9c866963..b640c7d2 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializer.java @@ -10,8 +10,6 @@ */ public class ToStringSnapshotSerializer implements SnapshotSerializer { - public static final String FORMAT = "string"; - @Override public String apply(Object[] objects) { return "[\n" + Arrays.stream(objects).map(Object::toString).collect(Collectors.joining("\n")) + "\n]"; @@ -19,6 +17,6 @@ public String apply(Object[] objects) { @Override public String getOutputFormat() { - return FORMAT; + return SerializerType.TEXT.name(); } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java index 2c16467b..de44d98f 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/ToStringSnapshotConfig.java @@ -1,7 +1,7 @@ package au.com.origin.snapshots.config; +import au.com.origin.snapshots.serializers.SerializerType; import au.com.origin.snapshots.serializers.SnapshotSerializer; -import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; import java.util.Arrays; import java.util.stream.Collectors; @@ -13,7 +13,7 @@ public SnapshotSerializer getSerializer() { return new SnapshotSerializer() { @Override public String getOutputFormat() { - return ToStringSnapshotSerializer.FORMAT; + return SerializerType.TEXT.name(); } @Override diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java index 4809dff7..20ddc70f 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java @@ -1,8 +1,7 @@ package au.com.origin.snapshots.reporters; import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import au.com.origin.snapshots.serializers.JacksonSnapshotSerializer; -import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; +import au.com.origin.snapshots.serializers.SerializerType; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; @@ -13,8 +12,8 @@ class PlainTextSnapshotDiffReporterTest { @Test void shouldSupportAllFormats() { - Assertions.assertThat(REPORTER.supportsFormat(ToStringSnapshotSerializer.FORMAT)).isTrue(); - Assertions.assertThat(REPORTER.supportsFormat(JacksonSnapshotSerializer.FORMAT)).isTrue(); + Assertions.assertThat(REPORTER.supportsFormat(SerializerType.TEXT.name())).isTrue(); + Assertions.assertThat(REPORTER.supportsFormat(SerializerType.JSON.name())).isTrue(); Assertions.assertThat(REPORTER.supportsFormat("xml")).isTrue(); Assertions.assertThat(REPORTER.supportsFormat("blah")).isTrue(); diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java index 0a38c54f..9e9e4073 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/DeterministicJacksonSnapshotSerializerTest.java @@ -31,7 +31,7 @@ public void shouldSerializeDifferentTypes() { @Test void shouldSupportJsonFormat() { - Assertions.assertThat(JacksonSnapshotSerializer.FORMAT).isEqualTo("json"); + Assertions.assertThat(new DeterministicJacksonSnapshotSerializer().getOutputFormat()).isEqualTo(SerializerType.JSON.name()); } private enum AnEnum { diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java index 2af2cca1..74f3b399 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/JacksonSnapshotSerializerTest.java @@ -31,7 +31,7 @@ public void shouldSerializeDifferentTypes() { @Test void shouldSupportJsonFormat() { - Assertions.assertThat(JacksonSnapshotSerializer.FORMAT).isEqualTo("json"); + Assertions.assertThat(new JacksonSnapshotSerializer().getOutputFormat()).isEqualTo(SerializerType.JSON.name()); } private enum AnEnum { diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java index ecfb8962..ddd4c0e9 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/LowercaseToStringSerializer.java @@ -11,6 +11,6 @@ public String apply(Object[] objects) { @Override public String getOutputFormat() { - return ToStringSnapshotSerializer.FORMAT; + return SerializerType.TEXT.name(); } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java index c3ecd3b2..935d6ffb 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/ToStringSnapshotSerializerTest.java @@ -36,7 +36,7 @@ void canUseSnapshotConfigAnnotationAtClassLevel() { @Test void shouldSupportStringFormat() { - Assertions.assertThat(ToStringSnapshotSerializer.FORMAT).isEqualTo("string"); + Assertions.assertThat(new ToStringSnapshotSerializer().getOutputFormat()).isEqualTo(SerializerType.TEXT.name()); } @AllArgsConstructor diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java index 97078775..843b14e7 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/serializers/UppercaseToStringSerializer.java @@ -11,6 +11,6 @@ public String apply(Object[] objects) { @Override public String getOutputFormat() { - return ToStringSnapshotSerializer.FORMAT; + return SerializerType.TEXT.name(); } } From 06ef103ffd3da84b8196bbe7ad20ecb276045329 Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 11:56:16 +0530 Subject: [PATCH 05/13] Add an isCi check to prevent creation of snapshots if running on a CI environment --- .../au/com/origin/snapshots/Snapshot.java | 36 ++++++++++++------- .../com/origin/snapshots/SnapshotConfig.java | 32 +++++++++++++---- .../PlainTextEqualsComparator.java | 2 +- .../comparators/SnapshotComparator.java | 2 +- ...er.java => PlainTextSnapshotReporter.java} | 4 +-- .../reporters/SnapshotDiffReporter.java | 8 ----- .../snapshots/reporters/SnapshotReporter.java | 8 +++++ .../au/com/origin/snapshots/SnapshotTest.java | 22 ++++++++++++ .../PlainTextEqualsComparatorTest.java | 4 +-- .../snapshots/config/BaseSnapshotConfig.java | 5 +++ ...ava => PlainTextSnapshotReporterTest.java} | 6 ++-- 11 files changed, 93 insertions(+), 36 deletions(-) rename java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/{PlainTextSnapshotDiffReporter.java => PlainTextSnapshotReporter.java} (87%) delete mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java create mode 100644 java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotReporter.java rename java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/{PlainTextSnapshotDiffReporterTest.java => PlainTextSnapshotReporterTest.java} (81%) diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java index 0356b5ab..47eb2f97 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java @@ -2,7 +2,7 @@ import au.com.origin.snapshots.comparators.SnapshotComparator; import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import au.com.origin.snapshots.reporters.SnapshotDiffReporter; +import au.com.origin.snapshots.reporters.SnapshotReporter; import au.com.origin.snapshots.serializers.DeterministicJacksonSnapshotSerializer; import au.com.origin.snapshots.serializers.JacksonSnapshotSerializer; import au.com.origin.snapshots.serializers.SnapshotSerializer; @@ -11,12 +11,14 @@ import lombok.AllArgsConstructor; import lombok.SneakyThrows; import lombok.With; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.lang.reflect.Method; import java.util.*; import java.util.stream.Collectors; +@Slf4j @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Snapshot { @@ -25,13 +27,14 @@ public class Snapshot { private final Class testClass; private final Method testMethod; private final Object[] current; + private final boolean isCi; @With private final SnapshotSerializer snapshotSerializer; @With private final SnapshotComparator snapshotComparator; @With - private final List snapshotDiffReporters; + private final List snapshotReporters; @With private final String scenario; @@ -46,9 +49,10 @@ public class Snapshot { testClass, testMethod, current, + snapshotConfig.isCi(), snapshotConfig.getSerializer(), snapshotConfig.getComparator(), - snapshotConfig.getDiffReporters(), + snapshotConfig.getReporters(), null); } @@ -86,14 +90,14 @@ public Snapshot comparator(SnapshotComparator comparator) { } /** - * Apply a list of custom diff reporters for this snapshot + * Apply a list of custom reporters for this snapshot * This will replace the default reporters defined in the config * - * @param diffReporters your custom diff reporters + * @param reporters your custom reporters * @return Snapshot */ - public Snapshot reporters(SnapshotDiffReporter... diffReporters) { - return this.withSnapshotDiffReporters(Arrays.asList(diffReporters)); + public Snapshot reporters(SnapshotReporter... reporters) { + return this.withSnapshotReporters(Arrays.asList(reporters)); } /** @@ -146,10 +150,10 @@ public void toMatchSnapshot() { if (rawSnapshot != null) { // Match existing Snapshot - if (!snapshotComparator.match(getSnapshotName(), rawSnapshot, currentObject)) { + if (!snapshotComparator.matches(getSnapshotName(), rawSnapshot, currentObject)) { snapshotFile.createDebugFile(currentObject.trim()); - List reporters = snapshotDiffReporters + List reporters = snapshotReporters .stream() .filter(reporter -> reporter.supportsFormat(snapshotSerializer.getOutputFormat())) .collect(Collectors.toList()); @@ -159,15 +163,21 @@ public void toMatchSnapshot() { throw new IllegalStateException("No compatible reporters found for comparator " + comparator); } - for (SnapshotDiffReporter reporter : reporters) { - reporter.reportDiff(getSnapshotName(), rawSnapshot, currentObject); + for (SnapshotReporter reporter : reporters) { + reporter.report(getSnapshotName(), rawSnapshot, currentObject); } throw new SnapshotMatchException("Error matching snapshot"); } } else { - // Create New Snapshot - snapshotFile.push(currentObject); + if (this.isCi) { + log.warn("We detected you are running on a CI Server - if this is incorrect please override the isCI() method in SnapshotConfig"); + throw new SnapshotMatchException("Snapshot [" + getSnapshotName() + "] not found. Has this snapshot been committed ?"); + } else { + log.warn("We detected you are running on a developer machine - if this is incorrect please override the isCI() method in SnapshotConfig"); + // Create New Snapshot + snapshotFile.push(currentObject); + } } snapshotFile.deleteDebugFile(); } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java index ac56b177..b50aa7ca 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java @@ -2,10 +2,11 @@ import au.com.origin.snapshots.comparators.PlainTextEqualsComparator; import au.com.origin.snapshots.comparators.SnapshotComparator; -import au.com.origin.snapshots.reporters.PlainTextSnapshotDiffReporter; -import au.com.origin.snapshots.reporters.SnapshotDiffReporter; +import au.com.origin.snapshots.reporters.PlainTextSnapshotReporter; +import au.com.origin.snapshots.reporters.SnapshotReporter; import au.com.origin.snapshots.serializers.SnapshotSerializer; import au.com.origin.snapshots.serializers.ToStringSnapshotSerializer; +import org.apache.commons.lang3.StringUtils; import java.lang.reflect.Method; import java.util.Collections; @@ -108,7 +109,6 @@ default String onSaveSnapshotFile(Class testClass, String snapshotContent) { return snapshotContent; } - /** * Optional * Override to supply your own custom comparator function @@ -121,7 +121,7 @@ default SnapshotComparator getComparator() { /** * Optional - * Override to supply your own custom diff reporter functions + * Override to supply your own custom reporter functions * Reporters will run in the same sequence as provided. * Reporters can choose to throw exceptions in which case subsequent reporters will be skipped. * Typically one such reporter can be included at the end which can use common assertion libraries @@ -133,7 +133,27 @@ default SnapshotComparator getComparator() { * * @return custom reporter functions */ - default List getDiffReporters() { - return Collections.singletonList(new PlainTextSnapshotDiffReporter()); + default List getReporters() { + return Collections.singletonList(new PlainTextSnapshotReporter()); + } + + /** + * Optional + * This method is meant to detect if we're running on a CI environment. + * This is used to determine the action to be taken when a snapshot is not found. + * + * If this method returns false, meaning we're NOT running on a CI environment (probably a dev machine), + * a new snapshot is created when not found. + * + * If this method returns true, meaning we're running on a CI environment, no new snapshots are created + * and an error is thrown instead to prevent tests from silently passing when snapshots are not found. + * + * Default logic to determine if running on a CI environment is to check for the presence of a 'CI' env variable + * Override this method if a custom CI detection logic is required. + * + * @return boolean indicating if we're running on a CI environment or not + */ + default boolean isCi() { + return StringUtils.isNotEmpty(System.getenv("CI")); } } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java index d55be463..e4a9ee91 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparator.java @@ -3,7 +3,7 @@ public class PlainTextEqualsComparator implements SnapshotComparator { @Override - public boolean match(String snapshotName, String rawSnapshot, String currentObject) { + public boolean matches(String snapshotName, String rawSnapshot, String currentObject) { return rawSnapshot.trim().equals(currentObject.trim()); } } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java index 912f0036..e49dd46c 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/comparators/SnapshotComparator.java @@ -1,5 +1,5 @@ package au.com.origin.snapshots.comparators; public interface SnapshotComparator { - boolean match(String snapshotName, String rawSnapshot, String currentObject); + boolean matches(String snapshotName, String rawSnapshot, String currentObject); } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java similarity index 87% rename from java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java rename to java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java index 21b01528..8c68ceca 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporter.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java @@ -7,7 +7,7 @@ import java.util.Arrays; import java.util.function.Supplier; -public class PlainTextSnapshotDiffReporter implements SnapshotDiffReporter { +public class PlainTextSnapshotReporter implements SnapshotReporter { private static final Supplier NO_DIFF_EXCEPTION_SUPPLIER = () -> new IllegalStateException("No differences found. Potential mismatch between comparator and reporter"); @@ -18,7 +18,7 @@ public boolean supportsFormat(String outputFormat) { } @Override - public void reportDiff(String snapshotName, String rawSnapshot, String currentObject) { + public void report(String snapshotName, String rawSnapshot, String currentObject) { Patch patch = DiffUtils.diff( Arrays.asList(rawSnapshot.trim().split("\n")), Arrays.asList(currentObject.trim().split("\n"))); diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java deleted file mode 100644 index 91f5087d..00000000 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotDiffReporter.java +++ /dev/null @@ -1,8 +0,0 @@ -package au.com.origin.snapshots.reporters; - -public interface SnapshotDiffReporter { - - boolean supportsFormat(String outputFormat); - - void reportDiff(String snapshotName, String rawSnapshot, String currentObject); -} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotReporter.java new file mode 100644 index 00000000..9e2ee0b8 --- /dev/null +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/SnapshotReporter.java @@ -0,0 +1,8 @@ +package au.com.origin.snapshots.reporters; + +public interface SnapshotReporter { + + boolean supportsFormat(String outputFormat); + + void report(String snapshotName, String rawSnapshot, String currentObject); +} diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java index 44850543..05a8f6ee 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java @@ -4,6 +4,7 @@ import au.com.origin.snapshots.exceptions.SnapshotMatchException; import au.com.origin.snapshots.serializers.JacksonSnapshotSerializer; import lombok.SneakyThrows; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -125,4 +126,25 @@ void shouldOverwriteSnapshotsWhenParamIsPassed() { Mockito.verify(snapshotFile) .push("java.lang.String.toString[hello world]=[\n" + " \"anyObject\"\n" + "]"); } + + @SneakyThrows + @Test + void shouldFailWhenRunningOnCiWithoutExistingSnapshot() { + BaseSnapshotConfig ciSnapshotConfig = new BaseSnapshotConfig() { + @Override + public boolean isCi() { + return true; + } + }; + + Snapshot ciSnapshot = new Snapshot( + ciSnapshotConfig, + new SnapshotFile(ciSnapshotConfig.getOutputDir(), "blah", this.getClass(), (a, b) -> b), + String.class, + String.class.getDeclaredMethod("toString"), + "anyObject"); + + Assertions.assertThatThrownBy(ciSnapshot::toMatchSnapshot) + .hasMessage("Snapshot [java.lang.String.toString=] not found. Has this snapshot been committed ?"); + } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java index 79a3c8fc..561a7e01 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/comparators/PlainTextEqualsComparatorTest.java @@ -9,11 +9,11 @@ class PlainTextEqualsComparatorTest { @Test void successfulComparison() { - Assertions.assertThat(COMPARATOR.match("snap1", "blah", "blah")).isTrue(); + Assertions.assertThat(COMPARATOR.matches("snap1", "blah", "blah")).isTrue(); } @Test void failingComparison() { - Assertions.assertThat(COMPARATOR.match("snap1", "blah", "blahblah")).isFalse(); + Assertions.assertThat(COMPARATOR.matches("snap1", "blah", "blahblah")).isFalse(); } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java index b4db3541..c9281ebd 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java @@ -66,4 +66,9 @@ public Method getTestMethod(Class testClass) { public SnapshotSerializer getSerializer() { return new JacksonSnapshotSerializer(); } + + @Override + public boolean isCi() { + return false; + } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java similarity index 81% rename from java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java rename to java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java index 20ddc70f..edfe37cf 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotDiffReporterTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java @@ -7,8 +7,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -class PlainTextSnapshotDiffReporterTest { - private static final PlainTextSnapshotDiffReporter REPORTER = new PlainTextSnapshotDiffReporter(); +class PlainTextSnapshotReporterTest { + private static final PlainTextSnapshotReporter REPORTER = new PlainTextSnapshotReporter(); @Test void shouldSupportAllFormats() { @@ -22,7 +22,7 @@ void shouldSupportAllFormats() { @Test void doReport() { assertThatExceptionOfType(SnapshotMatchException.class) - .isThrownBy(() -> REPORTER.reportDiff("snap1", "blah", "bloo")) + .isThrownBy(() -> REPORTER.report("snap1", "blah", "bloo")) .withMessageContaining("expecting:") .withMessageContaining("[\"blah\"]") .withMessageContaining("but was:") From 72d32d87e57fe1740d1a4473e4fd64c8f57d84dd Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 15:21:37 +0530 Subject: [PATCH 06/13] Use opentest4j to add IDE comparison support --- java-snapshot-testing-core/build.gradle | 3 +- .../au/com/origin/snapshots/Snapshot.java | 22 ++++-- .../com/origin/snapshots/SnapshotConfig.java | 2 +- .../exceptions/SnapshotMatchException.java | 21 +++++- .../au/com/origin/snapshots/SnapshotTest.java | 73 +++++++++++++++++-- .../snapshots/config/BaseSnapshotConfig.java | 2 +- 6 files changed, 104 insertions(+), 19 deletions(-) diff --git a/java-snapshot-testing-core/build.gradle b/java-snapshot-testing-core/build.gradle index bd735ef1..50045ac3 100644 --- a/java-snapshot-testing-core/build.gradle +++ b/java-snapshot-testing-core/build.gradle @@ -12,6 +12,7 @@ dependencies { implementation 'org.assertj:assertj-core:3.11.1' implementation 'org.apache.commons:commons-lang3:3.8.1' implementation 'org.slf4j:slf4j-api:2.0.0-alpha0' + implementation 'org.opentest4j:opentest4j:1.1.1' testImplementation 'org.slf4j:slf4j-simple:2.0.0-alpha0' testImplementation 'org.mockito:mockito-junit-jupiter:2.23.0' @@ -32,4 +33,4 @@ publishing { artifactId 'java-snapshot-testing-core' } } -} \ No newline at end of file +} diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java index 47eb2f97..bbff771a 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/Snapshot.java @@ -23,11 +23,13 @@ public class Snapshot { private final SnapshotConfig snapshotConfig; + @With(AccessLevel.PACKAGE) private final SnapshotFile snapshotFile; private final Class testClass; private final Method testMethod; + @With(AccessLevel.PACKAGE) private final Object[] current; - private final boolean isCi; + private final boolean isCI; @With private final SnapshotSerializer snapshotSerializer; @@ -49,7 +51,7 @@ public class Snapshot { testClass, testMethod, current, - snapshotConfig.isCi(), + snapshotConfig.isCI(), snapshotConfig.getSerializer(), snapshotConfig.getComparator(), snapshotConfig.getReporters(), @@ -163,15 +165,23 @@ public void toMatchSnapshot() { throw new IllegalStateException("No compatible reporters found for comparator " + comparator); } + List errors = new ArrayList<>(); + for (SnapshotReporter reporter : reporters) { - reporter.report(getSnapshotName(), rawSnapshot, currentObject); + try { + reporter.report(getSnapshotName(), rawSnapshot, currentObject); + } catch (Throwable t) { + errors.add(t); + } } - throw new SnapshotMatchException("Error matching snapshot"); + if(!errors.isEmpty()) { + throw new SnapshotMatchException("Error(s) matching snapshot(s)", errors); + } } } else { - if (this.isCi) { - log.warn("We detected you are running on a CI Server - if this is incorrect please override the isCI() method in SnapshotConfig"); + if (this.isCI) { + log.error("We detected you are running on a CI Server - if this is incorrect please override the isCI() method in SnapshotConfig"); throw new SnapshotMatchException("Snapshot [" + getSnapshotName() + "] not found. Has this snapshot been committed ?"); } else { log.warn("We detected you are running on a developer machine - if this is incorrect please override the isCI() method in SnapshotConfig"); diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java index b50aa7ca..59073ff6 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java @@ -153,7 +153,7 @@ default List getReporters() { * * @return boolean indicating if we're running on a CI environment or not */ - default boolean isCi() { + default boolean isCI() { return StringUtils.isNotEmpty(System.getenv("CI")); } } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java index 0209c063..c4901b4b 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java @@ -1,12 +1,27 @@ package au.com.origin.snapshots.exceptions; -public class SnapshotMatchException extends RuntimeException { +import org.opentest4j.AssertionFailedError; +import org.opentest4j.MultipleFailuresError; + +import java.util.Collections; +import java.util.List; + +public class SnapshotMatchException extends MultipleFailuresError { public SnapshotMatchException(String message) { - super(message); + super(message, Collections.emptyList()); } public SnapshotMatchException(String message, Throwable cause) { - super(message, cause); + super(message, Collections.singletonList(cause)); + } + + public SnapshotMatchException(String message, List causes) { + super(message, causes); + } + + // for a single failure with difference + public static SnapshotMatchException of(String message, Object expected, Object actual) { + return new SnapshotMatchException(message, new AssertionFailedError(message, expected, actual)); } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java index 05a8f6ee..6b1813d4 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java @@ -2,8 +2,9 @@ import au.com.origin.snapshots.config.BaseSnapshotConfig; import au.com.origin.snapshots.exceptions.SnapshotMatchException; -import au.com.origin.snapshots.serializers.JacksonSnapshotSerializer; -import lombok.SneakyThrows; +import au.com.origin.snapshots.reporters.SnapshotReporter; +import au.com.origin.snapshots.serializers.*; +import lombok.*; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -15,10 +16,8 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; +import java.util.function.BiConsumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -132,7 +131,7 @@ void shouldOverwriteSnapshotsWhenParamIsPassed() { void shouldFailWhenRunningOnCiWithoutExistingSnapshot() { BaseSnapshotConfig ciSnapshotConfig = new BaseSnapshotConfig() { @Override - public boolean isCi() { + public boolean isCI() { return true; } }; @@ -147,4 +146,64 @@ public boolean isCi() { Assertions.assertThatThrownBy(ciSnapshot::toMatchSnapshot) .hasMessage("Snapshot [java.lang.String.toString=] not found. Has this snapshot been committed ?"); } + + @SneakyThrows + @Test + void shouldAggregateMultipleFailures() { + SnapshotFile snapshotFile = Mockito.mock(SnapshotFile.class); + Set set = new HashSet<>(); + set.add("java.lang.String.toString=[\n \"hello\"\n]"); + Mockito.when(snapshotFile.getRawSnapshots()).thenReturn(set); + + Stream> reportingFunctions = Stream.of( + (rawSnapshot, currentObject) -> assertThat(currentObject).isEqualTo(rawSnapshot), // assertj + org.junit.jupiter.api.Assertions::assertEquals, // junit jupiter + (rawSnapshot, currentObject) -> { + String message = String.join(System.lineSeparator(), + "Expected : ", rawSnapshot, "Actual : ", currentObject); + throw SnapshotMatchException.of(message, rawSnapshot, currentObject); // SnapshotMatchException delegating to opentest4j + } + ); + + Stream reporters = reportingFunctions.map(consumer -> new SnapshotReporter() { + @Override + public boolean supportsFormat(String outputFormat) { + return true; + } + + @Override + public void report(String snapshotName, String rawSnapshot, String currentObject) { + consumer.accept(rawSnapshot, currentObject); + } + }); + + Snapshot failingSnapshot = snapshot + .withCurrent(new Object[]{"hola"}) + .withSnapshotFile(snapshotFile) + .serializer(new DeterministicJacksonSnapshotSerializer()) + .reporters(reporters.toArray(SnapshotReporter[]::new)); + + try { + failingSnapshot.toMatchSnapshot(); + } + catch (Throwable m) { + String cleanMessage = m.getMessage() + .replace("<\"", "") + .replace("<", "") + .replace("\n", "") + .replace("\">", " ") + .replace(">", " ") + .replace("]", "") + .replace("java.lang.String.toString=[", "") + .replaceAll(" +", " "); + + assertThat(cleanMessage).contains("Expecting: \"hola\" to be equal to: \"hello\" but was not."); // assertj + assertThat(cleanMessage).contains("expected: \"hello\" but was: \"hola\""); // junit jupiter + assertThat(cleanMessage).contains("Expected : \"hello\"Actual : \"hola\""); // SnapshotMatchException delegating to opentest4j + + return; + } + + Assertions.fail("Expected an error to be thrown"); + } } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java index c9281ebd..1995bdb0 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/config/BaseSnapshotConfig.java @@ -68,7 +68,7 @@ public SnapshotSerializer getSerializer() { } @Override - public boolean isCi() { + public boolean isCI() { return false; } } From 736452f9a5c406ac3206784e8021f087deb9bb2a Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 15:28:29 +0530 Subject: [PATCH 07/13] Change hard-coded "\n" to System.lineSeparator() --- .../src/test/java/au/com/origin/snapshots/SnapshotTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java index 6b1813d4..25bdeb7a 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java @@ -190,7 +190,7 @@ public void report(String snapshotName, String rawSnapshot, String currentObject String cleanMessage = m.getMessage() .replace("<\"", "") .replace("<", "") - .replace("\n", "") + .replace(System.lineSeparator(), "") .replace("\">", " ") .replace(">", " ") .replace("]", "") From f4c98a21428aee20532031428ed80a03267bc4e8 Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 16:05:27 +0530 Subject: [PATCH 08/13] Remove tab escape character to fix test breaking on windows --- .../src/test/java/au/com/origin/snapshots/SnapshotTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java index 25bdeb7a..cb93e798 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java @@ -190,7 +190,8 @@ public void report(String snapshotName, String rawSnapshot, String currentObject String cleanMessage = m.getMessage() .replace("<\"", "") .replace("<", "") - .replace(System.lineSeparator(), "") + .replace("\n", "") + .replace("\t", "") .replace("\">", " ") .replace(">", " ") .replace("]", "") From a69358f15523e3762b8b28067be540899606a423 Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 16:10:17 +0530 Subject: [PATCH 09/13] Use regexes to verify failure messages --- .../src/test/java/au/com/origin/snapshots/SnapshotTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java index cb93e798..2c9f2969 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java @@ -198,9 +198,9 @@ public void report(String snapshotName, String rawSnapshot, String currentObject .replace("java.lang.String.toString=[", "") .replaceAll(" +", " "); - assertThat(cleanMessage).contains("Expecting: \"hola\" to be equal to: \"hello\" but was not."); // assertj - assertThat(cleanMessage).contains("expected: \"hello\" but was: \"hola\""); // junit jupiter - assertThat(cleanMessage).contains("Expected : \"hello\"Actual : \"hola\""); // SnapshotMatchException delegating to opentest4j + assertThat(cleanMessage).containsPattern("Expecting.*hola.*to be equal to.*hello.*but was not"); // assertj + assertThat(cleanMessage).containsPattern("expected.*hello.*but was.*hola"); // junit jupiter + assertThat(cleanMessage).containsPattern("Expected.*hello.*Actual.*hola"); // SnapshotMatchException delegating to opentest4j return; } From 572b3525674c463cd113edc129776bad891af198 Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 16:19:49 +0530 Subject: [PATCH 10/13] Use escape characters as regex --- .../src/test/java/au/com/origin/snapshots/SnapshotTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java index 2c9f2969..4f62d166 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java @@ -190,8 +190,9 @@ public void report(String snapshotName, String rawSnapshot, String currentObject String cleanMessage = m.getMessage() .replace("<\"", "") .replace("<", "") - .replace("\n", "") - .replace("\t", "") + .replaceAll("\n", "") + .replaceAll("\r", "") + .replaceAll("\t", "") .replace("\">", " ") .replace(">", " ") .replace("]", "") From 6d1d6e2df0c7dbcf7e9453c391d9e5fe6bc79adc Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 17:22:37 +0530 Subject: [PATCH 11/13] Fix javadoc --- .../java/au/com/origin/snapshots/SnapshotConfig.java | 11 ++++------- .../snapshots/exceptions/SnapshotMatchException.java | 6 ------ .../reporters/PlainTextSnapshotReporter.java | 7 +++++-- .../java/au/com/origin/snapshots/SnapshotTest.java | 5 +++-- .../reporters/PlainTextSnapshotReporterTest.java | 4 ++-- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java index 59073ff6..66e2ba6c 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/SnapshotConfig.java @@ -123,13 +123,10 @@ default SnapshotComparator getComparator() { * Optional * Override to supply your own custom reporter functions * Reporters will run in the same sequence as provided. - * Reporters can choose to throw exceptions in which case subsequent reporters will be skipped. - * Typically one such reporter can be included at the end which can use common assertion libraries - * like assertj or junit assertions to 'fail' the test. This is useful since IDEs like intellij can - * then show a diff using their native diff tools. - *

- * In the absence of any 'throwing' reporters in the list, a default failure exception will be thrown - * to make sure tests fail. + * Reporters should throw exceptions to indicate comparison failure. + * Exceptions thrown from reporters are aggregated and reported together. + * Reporters that wish to leverage IDE comparison tools can use standard + * assertion libraries like assertj, junit jupiter assertions (or) opentest4j. * * @return custom reporter functions */ diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java index c4901b4b..78fcaabd 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/exceptions/SnapshotMatchException.java @@ -1,6 +1,5 @@ package au.com.origin.snapshots.exceptions; -import org.opentest4j.AssertionFailedError; import org.opentest4j.MultipleFailuresError; import java.util.Collections; @@ -19,9 +18,4 @@ public SnapshotMatchException(String message, Throwable cause) { public SnapshotMatchException(String message, List causes) { super(message, causes); } - - // for a single failure with difference - public static SnapshotMatchException of(String message, Object expected, Object actual) { - return new SnapshotMatchException(message, new AssertionFailedError(message, expected, actual)); - } } diff --git a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java index 8c68ceca..1bb6c673 100644 --- a/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java +++ b/java-snapshot-testing-core/src/main/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporter.java @@ -1,8 +1,8 @@ package au.com.origin.snapshots.reporters; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; import org.assertj.core.util.diff.DiffUtils; import org.assertj.core.util.diff.Patch; +import org.opentest4j.AssertionFailedError; import java.util.Arrays; import java.util.function.Supplier; @@ -22,7 +22,10 @@ public void report(String snapshotName, String rawSnapshot, String currentObject Patch patch = DiffUtils.diff( Arrays.asList(rawSnapshot.trim().split("\n")), Arrays.asList(currentObject.trim().split("\n"))); - throw new SnapshotMatchException("Error on: \n" + currentObject.trim() + "\n\n" + getDiffString(patch)); + + String message = "Error on: \n" + currentObject.trim() + "\n\n" + getDiffString(patch); + + throw new AssertionFailedError(message, rawSnapshot, currentObject); } public static String getDiffString(Patch patch) { diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java index 4f62d166..736353b9 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/SnapshotTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import org.opentest4j.AssertionFailedError; import java.io.IOException; import java.nio.file.Files; @@ -161,7 +162,7 @@ void shouldAggregateMultipleFailures() { (rawSnapshot, currentObject) -> { String message = String.join(System.lineSeparator(), "Expected : ", rawSnapshot, "Actual : ", currentObject); - throw SnapshotMatchException.of(message, rawSnapshot, currentObject); // SnapshotMatchException delegating to opentest4j + throw new AssertionFailedError(message, rawSnapshot, currentObject); // opentest4j } ); @@ -201,7 +202,7 @@ public void report(String snapshotName, String rawSnapshot, String currentObject assertThat(cleanMessage).containsPattern("Expecting.*hola.*to be equal to.*hello.*but was not"); // assertj assertThat(cleanMessage).containsPattern("expected.*hello.*but was.*hola"); // junit jupiter - assertThat(cleanMessage).containsPattern("Expected.*hello.*Actual.*hola"); // SnapshotMatchException delegating to opentest4j + assertThat(cleanMessage).containsPattern("Expected.*hello.*Actual.*hola"); // opentest4j return; } diff --git a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java index edfe37cf..b505a714 100644 --- a/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java +++ b/java-snapshot-testing-core/src/test/java/au/com/origin/snapshots/reporters/PlainTextSnapshotReporterTest.java @@ -1,9 +1,9 @@ package au.com.origin.snapshots.reporters; -import au.com.origin.snapshots.exceptions.SnapshotMatchException; import au.com.origin.snapshots.serializers.SerializerType; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; +import org.opentest4j.AssertionFailedError; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -21,7 +21,7 @@ void shouldSupportAllFormats() { @Test void doReport() { - assertThatExceptionOfType(SnapshotMatchException.class) + assertThatExceptionOfType(AssertionFailedError.class) .isThrownBy(() -> REPORTER.report("snap1", "blah", "bloo")) .withMessageContaining("expecting:") .withMessageContaining("[\"blah\"]") From d675ee833bfa4b49e99eea04ae48b8a03eab7a14 Mon Sep 17 00:00:00 2001 From: Juja Date: Sun, 22 Nov 2020 18:18:16 +0530 Subject: [PATCH 12/13] Update README.md with examples --- README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/README.md b/README.md index 7b1f65f5..5701dd2f 100644 --- a/README.md +++ b/README.md @@ -317,6 +317,69 @@ public class HibernateSnapshotSerializer implements SnapshotSerializer { } ``` +## Supplying a custom SnapshotComparator +The comparator determines if two snapshots match. + +Currently, we support one default comparator (`PlainTextEqualsComparator`) which uses string equals for comparison. + +This should work for most cases. Custom implementations of `SnapshotComparator` can provide more advanced comparisons. + +Comparators follow the same resolution order as Serializers +1. method +2. class +3. global + +### Example: JsonObjectComparator +The default comparator may be too strict for certain types of data. +For example, when comparing json objects, formatting of the json string or the order of fields +may not be of much importance during comparison. A custom serializer can help in such cases. + +For example, the following will convert a json string to a Map and then perform an equals comparison +so that formatting and field order are ignored. + +```java +public class JsonObjectComparator implements SnapshotComparator { + @Override + public boolean matches(String snapshotName, String rawSnapshot, String currentObject) { + return asObject(snapshotName, rawSnapshot).equals(asObject(snapshotName, currentObject)); + } + + @SneakyThrows + private static Object asObject(String snapshotName, String json) { + return new ObjectMapper().readValue(json.replaceFirst(snapshotName, ""), Object.class); + } +} +``` + +## Supplying a custom SnapshotReporter +The reporter reports the details of comparison failures. + +Currently, we support one default reporter (`PlainTextSnapshotReporter`) which uses assertj's DiffUtils +to generate a patch of the differences between two snapshots. + +Custom reporters can be plugged in by implementing `SnapshotReporter`. + +Reporters follow the same resolution order as Serializers and Comparators + +### Example: JsonDiffReporter +For generating and reporting json diffs using other libraries like https://github.com/skyscreamer/JSONassert +a custom reporter can be created like the one below. + +```java +public class JsonAssertReporter implements SnapshotReporter { + @Override + public boolean supportsFormat(String outputFormat) { + return SerializerType.JSON.name().equalsIgnoreCase(outputFormat); + } + + @Override + @SneakyThrows + public void report(String snapshotName, String rawSnapshot, String currentObject) { + JSONAssert.assertEquals(rawSnapshot, currentObject, JSONCompareMode.STRICT); + } +} +``` + ## Supplying a custom SnapshotConfig You can override the snapshot configuration easily using the `@UseSnapshotConfig` annotation From b174f73d9cebf6e40fe70fb87ab76eefcc837da0 Mon Sep 17 00:00:00 2001 From: Juja Date: Mon, 23 Nov 2020 21:03:25 +0530 Subject: [PATCH 13/13] serializer -> comparator in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5701dd2f..682edd79 100644 --- a/README.md +++ b/README.md @@ -332,7 +332,7 @@ Comparators follow the same resolution order as Serializers ### Example: JsonObjectComparator The default comparator may be too strict for certain types of data. For example, when comparing json objects, formatting of the json string or the order of fields -may not be of much importance during comparison. A custom serializer can help in such cases. +may not be of much importance during comparison. A custom comparator can help in such cases. For example, the following will convert a json string to a Map and then perform an equals comparison so that formatting and field order are ignored.