diff --git a/src/main/java/io/github/goatfryed/assert_baseline/BaselineAssertions.java b/src/main/java/io/github/goatfryed/assert_baseline/BaselineAssertions.java index 0f7a09f..f1d991e 100644 --- a/src/main/java/io/github/goatfryed/assert_baseline/BaselineAssertions.java +++ b/src/main/java/io/github/goatfryed/assert_baseline/BaselineAssertions.java @@ -19,4 +19,7 @@ public static XmlBaselineAssertion assertThatXml(String string) { return new XmlBaselineAssertion(new SerializableSubject(string)); } + public static SerializableBaselineAssertion assertThatSerializable(ACTUAL actual) { + return new SerializableBaselineAssertion<>(actual); + } } diff --git a/src/main/java/io/github/goatfryed/assert_baseline/serializable/SerializableBaselineAssertion.java b/src/main/java/io/github/goatfryed/assert_baseline/serializable/SerializableBaselineAssertion.java new file mode 100644 index 0000000..ac963d4 --- /dev/null +++ b/src/main/java/io/github/goatfryed/assert_baseline/serializable/SerializableBaselineAssertion.java @@ -0,0 +1,76 @@ +package io.github.goatfryed.assert_baseline.serializable; + +import io.github.goatfryed.assert_baseline.core.AbstractBaselineAssertion; +import io.github.goatfryed.assert_baseline.core.BaselineAssertionAdapter; +import io.github.goatfryed.assert_baseline.core.BaselineContext; +import org.assertj.core.api.Assert; +import org.assertj.core.api.ObjectAssert; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.function.Consumer; +import java.util.function.Function; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SerializableBaselineAssertion extends AbstractBaselineAssertion,ACTUAL> { + + private Function, Assert> assertConfig = assertion -> assertion; + + public SerializableBaselineAssertion(ACTUAL actual) { + super(actual, SerializableBaselineAssertion.class); + } + + /** + * This is the same as just calling
+ * + * serializableAssertion.satisfies(it -> assertThat(it)...) + *
+ * It's only implemented as a convenience method in line with our general pattern + */ + public SerializableBaselineAssertion serializableSatisfies(Consumer> assertion) { + assertion.accept(assertThat(actual)); + return myself; + } + + /** + * This yields control of the {@link Assert} to the user.
+ * Note that whatever assertion is returned gets called with isEqualTo(baselineObject). + */ + public SerializableBaselineAssertion usingSerializableComparator(Function, Assert> assertConfig) { + this.assertConfig = assertConfig; + return myself; + } + + @Override + protected @NotNull BaselineAssertionAdapter getAssertionAdapter() { + return new Adapter(); + } + + class Adapter implements BaselineAssertionAdapter { + @Override + public void writeActual(BaselineContext.ActualOutput output, BaselineContext context) { + try (var oos = new ObjectOutputStream(output.outputStream())) { + oos.writeObject(actual); + } catch (IOException e) { + throw new AssertionError("failed to serialize actual", e); + } + } + + @Override + public void assertEquals(BaselineContext.BaselineInput baseline, BaselineContext context) { + try (var ois = new ObjectInputStream(baseline.getInputStream())) { + var baselineObject = ois.readObject(); + assertConfig.apply(assertThat(actual)) + .isEqualTo(baselineObject); + } catch (IOException e) { + throw new AssertionError("failed to read baseline", e); + } catch (ClassNotFoundException e) { + throw new AssertionError("baseline class does not exist. This indicates that you changed class definition and need to recreate the baseline.", e); + } + } + } + +} diff --git a/src/test/java/io/github/goatfryed/assert_baseline/serializable/Foo.java b/src/test/java/io/github/goatfryed/assert_baseline/serializable/Foo.java new file mode 100644 index 0000000..d40cd42 --- /dev/null +++ b/src/test/java/io/github/goatfryed/assert_baseline/serializable/Foo.java @@ -0,0 +1,8 @@ +package io.github.goatfryed.assert_baseline.serializable; + +import java.io.Serializable; + +public record Foo(String foo, Bar fooBar) implements Serializable { + + public record Bar(String prop1, String prop2) implements Serializable { } +} diff --git a/src/test/java/io/github/goatfryed/assert_baseline/serializable/SerializableBaselineAssertionTest.java b/src/test/java/io/github/goatfryed/assert_baseline/serializable/SerializableBaselineAssertionTest.java new file mode 100644 index 0000000..396341c --- /dev/null +++ b/src/test/java/io/github/goatfryed/assert_baseline/serializable/SerializableBaselineAssertionTest.java @@ -0,0 +1,60 @@ +package io.github.goatfryed.assert_baseline.serializable; + +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.nio.file.Path; + +import static io.github.goatfryed.assert_baseline.BaselineAssertions.assertThatSerializable; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; + +class SerializableBaselineAssertionTest { + + public final static String BASE_PATH = "serializable/object.%s.json"; + public final static String ALTERED_PATH = BASE_PATH.formatted("altered"); + public final static String ACTUAL_PATH = BASE_PATH.formatted("actual"); + public final static String BASELINE_PATH = BASE_PATH.formatted("baseline"); + public final static Path RESOURCE_ROOT = Path.of("src/test/resources"); + public final static File ACTUAL_FILE = RESOURCE_ROOT.resolve(ACTUAL_PATH).toFile(); + public final static File ALTERED_FILE = RESOURCE_ROOT.resolve(ALTERED_PATH).toFile(); + public final static File BASELINE_FILE = RESOURCE_ROOT.resolve(BASELINE_PATH).toFile(); + + @Test + void itPassesSameContent() { + var foo = new Foo("chucky", new Foo.Bar("chuck","norris")); + + assertThatSerializable(foo) + .isEqualToBaseline(BASELINE_PATH); + } + + @Test + void itFailsContentViolations() { + var foo = new Foo("chucky", new Foo.Bar("chuck","the murder puppet")); + + assertThatCode(() -> + assertThatSerializable(foo) + .isEqualToBaseline(BASELINE_PATH) + ).hasMessageContaining("expected: Foo[foo=chucky, fooBar=Bar[prop1=chuck, prop2=norris]]"); + } + + @Test + void itPassesAllowedDifferences() { + var foo = new Foo("chucky", new Foo.Bar("chuck","the murder puppet")); + + assertThatSerializable(foo) + .usingSerializableComparator(assertion -> assertion + .usingRecursiveComparison() + .ignoringFields("foo", "fooBar.prop2") + ).isEqualToBaseline(BASELINE_PATH); + } + + @Test + void itInteropsWithAssertJ() { + var foo = new Foo("chucky", new Foo.Bar("chuck","norris")); + + assertThatSerializable(foo) + .serializableSatisfies(assertion -> assertion + .hasFieldOrProperty("fooBar") + ); + } +} \ No newline at end of file diff --git a/src/test/resources/serializable/object.baseline.json b/src/test/resources/serializable/object.baseline.json new file mode 100644 index 0000000..5e94512 Binary files /dev/null and b/src/test/resources/serializable/object.baseline.json differ