Skip to content

Commit

Permalink
feat(#3): add support for java's Serializable
Browse files Browse the repository at this point in the history
  • Loading branch information
goatfryed committed Oct 26, 2024
1 parent fa4b5be commit c6ea140
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ public static XmlBaselineAssertion assertThatXml(String string) {
return new XmlBaselineAssertion(new SerializableSubject(string));
}

public static <ACTUAL> SerializableBaselineAssertion<ACTUAL> assertThatSerializable(ACTUAL actual) {
return new SerializableBaselineAssertion<>(actual);
}
}
Original file line number Diff line number Diff line change
@@ -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<ACTUAL> extends AbstractBaselineAssertion<SerializableBaselineAssertion<ACTUAL>,ACTUAL> {

private Function<ObjectAssert<ACTUAL>, Assert<?,?>> assertConfig = assertion -> assertion;

public SerializableBaselineAssertion(ACTUAL actual) {
super(actual, SerializableBaselineAssertion.class);
}

/**
* This is the same as just calling<br>
* <code>
* serializableAssertion.satisfies(it -> assertThat(it)...)
* </code><br>
* It's only implemented as a convenience method in line with our general pattern
*/
public SerializableBaselineAssertion<ACTUAL> serializableSatisfies(Consumer<ObjectAssert<ACTUAL>> assertion) {
assertion.accept(assertThat(actual));
return myself;
}

/**
* This yields control of the {@link Assert} to the user.<br>
* Note that whatever assertion is returned gets called with <code>isEqualTo(baselineObject)</code>.
*/
public SerializableBaselineAssertion<ACTUAL> usingSerializableComparator(Function<ObjectAssert<ACTUAL>, 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);
}
}
}

}
Original file line number Diff line number Diff line change
@@ -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 { }
}
Original file line number Diff line number Diff line change
@@ -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")
);
}
}
Binary file added src/test/resources/serializable/object.baseline.json
Binary file not shown.

0 comments on commit c6ea140

Please sign in to comment.