Skip to content

Commit

Permalink
Upgrade to JUnit 5 and remove Mockito
Browse files Browse the repository at this point in the history
The choice to remove Mockito is because it was only used in one test.
  • Loading branch information
opwvhk committed Feb 20, 2024
1 parent 4ad1975 commit 8571eb3
Show file tree
Hide file tree
Showing 18 changed files with 278 additions and 253 deletions.
12 changes: 0 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -148,24 +148,12 @@
</dependency>

<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.25.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
Expand Down
9 changes: 7 additions & 2 deletions src/main/java/opwvhk/avro/io/AsAvroParserBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ protected List<ResolveRule<WriteSchema>> createResolveRules() {
private static final ThreadLocal<Map<Utils.Seen, ValueResolver>> SEEN = ThreadLocal.withInitial(HashMap::new);

/**
* <p>Create a {@code ValueResolver} that can resolve written values int the write schema into parsed values in the read schema.</p>
* <p>Create a {@code ValueResolver} that can resolve written values in the write schema into parsed values in the read schema.</p>
*
* <p>This method uses the rules returned by {@link #createResolveRules()}. Please note that these rules are explicitly encouraged to use this method to
* resolve elements of composite types. This method guards against infinite recursion, by using a delegating {@code ValueResolver} that receives a delegate
Expand All @@ -321,12 +321,17 @@ protected ValueResolver createResolver(WriteSchema writeSchema, Schema readSchem
}

if (resolveRules == null) {
// This method might (but should not) cause recursive calls to the current method.
resolveRules = createResolveRules();
}
for (ResolveRule<WriteSchema> rule : resolveRules) {
if (rule.test(writeSchema, readSchema)) {
// This method will (but may not) cause recursive calls to the current method.
ValueResolver resolver = requireNonNull(rule.createResolver(writeSchema, readSchema));
// the map contains the DelegatingResolver we put in above: if there's a different resolver for the schemaPair, we exit the method above.

// The map contains the DelegatingResolver we put in above (otherwise the method would have exited directly after that).
// Now we replace the DelegatingResolver with the created resolver, but we set it in the DelegatingResolver in case that was
// used due to recursion.
DelegatingResolver delegatingResolver = requireNonNull((DelegatingResolver) resolversForSeenSchemas.put(schemaPair, resolver));
delegatingResolver.setDelegate(resolver);
return resolver;
Expand Down
9 changes: 0 additions & 9 deletions src/main/java/opwvhk/avro/io/DelegatingResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@
public class DelegatingResolver extends ValueResolver {
private ValueResolver delegate;

/**
* Tell whether this resolver has a delegate to delegate method calls to.
*
* @return {@code true} if this resolver has a delegate, {@code false} otherwise
*/
public boolean hasDelegate() {
return delegate != null;
}

/**
* Set the delegate for this resolver.
*
Expand Down
20 changes: 10 additions & 10 deletions src/test/java/opwvhk/avro/SchemaManipulatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

import net.jimblackler.jsonschemafriend.GenerationException;
import org.apache.avro.Schema;
import org.junit.Test;
import org.junit.jupiter.api.Test;

import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;

public class SchemaManipulatorTest {
class SchemaManipulatorTest {
@Test
public void testSortedDocumentationViaXsd() throws IOException {
void testSortedDocumentationViaXsd() throws IOException {
StringBuilder markdown = new StringBuilder();

URL xsdLocation = getClass().getResource("xml/payload.xsd");
Expand Down Expand Up @@ -48,7 +48,7 @@ public void testSortedDocumentationViaXsd() throws IOException {
}

@Test
public void testSortedDocumentationViaJsonSchema() throws GenerationException, URISyntaxException, IOException {
void testSortedDocumentationViaJsonSchema() throws GenerationException, URISyntaxException, IOException {
StringBuilder markdown = new StringBuilder();

URL schemaLocation = getClass().getResource("json/TestRecord.schema.json");
Expand Down Expand Up @@ -87,7 +87,7 @@ public void testSortedDocumentationViaJsonSchema() throws GenerationException, U
}

@Test
public void testDocumentationViaAvro() throws IOException {
void testDocumentationViaAvro() throws IOException {
URL avroLocation = getClass().getResource("xml/envelope.avsc");
String markDownTable = SchemaManipulator.startFromAvro(avroLocation).asMarkdownTable();

Expand All @@ -104,7 +104,7 @@ public void testDocumentationViaAvro() throws IOException {
}

@Test
public void testManipulationsWithAliases() {
void testManipulationsWithAliases() {
// Note: manipulating by schema (and field name) also matches on aliases
Schema schema = SchemaManipulator.startFromAvro(SOURCE_SCHEMA)
.renameSchema("ns.envelope", "ns.satchel")
Expand All @@ -121,7 +121,7 @@ public void testManipulationsWithAliases() {
}

@Test
public void testManipulationsWithoutAliasesByPath() {
void testManipulationsWithoutAliasesByPath() {
// Note: manipulating by path cannot match on aliases
Schema schema = SchemaManipulator.startFromAvro(SOURCE_SCHEMA)
.renameWithoutAliases()
Expand All @@ -138,7 +138,7 @@ public void testManipulationsWithoutAliasesByPath() {
}

@Test
public void testUnwrappingArrays1() {
void testUnwrappingArrays1() {
Schema schema = SchemaManipulator.startFromAvro(SOURCE_SCHEMA_WITH_ARRAYS)
.unwrapArrayAtPath("matchByPath")
.unwrapArrays(3)
Expand All @@ -149,7 +149,7 @@ public void testUnwrappingArrays1() {
}

@Test
public void testUnwrappingArrays2() {
void testUnwrappingArrays2() {
Schema schema = SchemaManipulator.startFromAvro(SOURCE_SCHEMA_WITH_ARRAYS)
.unwrapArray("ns.WithArrays", "matchByName")
.unwrapArrays(3)
Expand All @@ -160,7 +160,7 @@ public void testUnwrappingArrays2() {
}

@Test
public void testManipulatingRecursiveSchemas() {
void testManipulatingRecursiveSchemas() {
// Note: manipulating by schema (and field name) also matches on aliases
Schema schema = SchemaManipulator.startFromAvro(SOURCE_RECURSIVE_SCHEMA)
.renameField("ns.recursive", "rabbitHole", "droste")
Expand Down
22 changes: 11 additions & 11 deletions src/test/java/opwvhk/avro/io/AsAvroParserBaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@
import opwvhk.avro.ResolvingFailure;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.junit.Test;
import org.junit.jupiter.api.Test;

import static java.time.ZoneOffset.UTC;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class AsAvroParserBaseTest {
class AsAvroParserBaseTest {
/*
NOTE: This test class is terribly incomplete, as the resolvers it creates cannot be tested without parsing (which this test doesn't cover).
*/

@Test
public void testTimeZoneOffsetDetermination() {
void testTimeZoneOffsetDetermination() {
Clock fixedClock = Clock.fixed(Instant.ofEpochMilli(1681720200000L), UTC); // 2023-04-17T08:30:00.000000000Z

assertThat(AsAvroParserBase.asOffset(UTC, fixedClock)).isEqualTo(ZoneOffset.ofHours(0));
Expand All @@ -30,7 +30,7 @@ public void testTimeZoneOffsetDetermination() {
}

@Test
public void testResolvingAllTypes() {
void testResolvingAllTypes() {
Schema schema = new Schema.Parser().parse("""
{"type": "record", "name": "AllTypes", "fields": [
{"name": "optionalBoolean", "type": ["null", "boolean"], "aliases": ["bool"]},
Expand Down Expand Up @@ -114,15 +114,15 @@ private Object resolveScalar(ValueResolver resolver, Object collector, String pr
}

@Test
public void testFailuresForUnmatchedBinaryData() {
void testFailuresForUnmatchedBinaryData() {
Schema bytesSchema = Schema.create(Schema.Type.BYTES);
AsAvroParserBase<Object> parserBase = new AsAvroParserBase<>(GenericData.get()) {};

assertThatThrownBy(() -> parserBase.createResolver(bytesSchema)).isInstanceOf(ResolvingFailure.class);
}

@Test
public void testParsingInvalidEnum() {
void testParsingInvalidEnum() {
AsAvroParserBase<?> parserBase = new AsAvroParserBase<>(GenericData.get()) {};

Schema enumWithDefault = Schema.createEnum("choice", null, null, List.of("maybe", "yes", "no"), "maybe");
Expand All @@ -137,13 +137,13 @@ public void testParsingInvalidEnum() {
}

@Test
public void coverMethodThatCannotBeCalled() {
void coverMethodThatCannotBeCalled() {
// There is no code path that actively causes this failure (that would mean a bug in building resolvers).
assertThatThrownBy(() -> new ValueResolver() {}.addContent(null, null)).isInstanceOf(IllegalStateException.class);
}

@Test
public void testRecordImplicitArrayFields() {
void testRecordImplicitArrayFields() {
ValueResolver sr = new ScalarValueResolver(s -> s);
Schema.Field f = new Schema.Field("texts", Schema.createArray(Schema.create(Schema.Type.STRING)));
RecordResolver rr = new RecordResolver(GenericData.get(), Schema.createRecord("Record", null, null, false, List.of(f)));
Expand All @@ -157,7 +157,7 @@ public void testRecordImplicitArrayFields() {
}

@Test
public void testRecordFieldDefaultValues() {
void testRecordFieldDefaultValues() {
ValueResolver sr = new ScalarValueResolver(s -> s);
Schema optionalString = Schema.createUnion(Schema.create(Schema.Type.STRING), Schema.create(Schema.Type.NULL));
Schema.Field f = new Schema.Field("value", optionalString, null, "missing");
Expand All @@ -176,7 +176,7 @@ public void testRecordFieldDefaultValues() {
}

@Test
public void testRecordContentField() {
void testRecordContentField() {
ValueResolver sr = new ScalarValueResolver(s -> s);
Schema.Field f = new Schema.Field("value", Schema.create(Schema.Type.STRING), null, "missing");
RecordResolver rr = new RecordResolver(GenericData.get(), Schema.createRecord("Record", null, null, false, List.of(f)));
Expand All @@ -189,7 +189,7 @@ public void testRecordContentField() {
}

@Test
public void testValueResolverContentParseFlag() {
void testValueResolverContentParseFlag() {
ValueResolver resolver = new ValueResolver(){};
assertThat(resolver.parseContent()).isTrue();
resolver.doNotParseContent();
Expand Down
116 changes: 79 additions & 37 deletions src/test/java/opwvhk/avro/io/DelegatingResolverTest.java
Original file line number Diff line number Diff line change
@@ -1,53 +1,95 @@
package opwvhk.avro.io;

import org.junit.Test;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import java.util.ArrayList;
import java.util.List;

public class DelegatingResolverTest {
import static org.assertj.core.api.Assertions.assertThat;

@Test
public void validateDelegation() {
ValueResolver delegate = mock(ValueResolver.class);
DelegatingResolver resolver = new DelegatingResolver();
class DelegatingResolverTest {

Object collector = new Object();
Object value = new Object();
@Test
void validateDelegation() {
DelegatingResolver resolver = new DelegatingResolver();

assertThat(resolver.hasDelegate()).isFalse();
MockResolver delegate = new MockResolver();
resolver.setDelegate(delegate);

resolver.setDelegate(delegate);
assertThat(resolver.hasDelegate()).isTrue();
Object collector = new Object();
Object value = new Object();

resolver.doNotParseContent();
verify(delegate).doNotParseContent();
verifyNoMoreInteractions(delegate);
resolver.doNotParseContent();
assertThat(delegate.calls()).containsExactly("doNotParseContent()");

resolver.resolve("name");
verify(delegate).resolve("name");
verifyNoMoreInteractions(delegate);
resolver.resolve("name");
assertThat(delegate.calls()).containsExactly("resolve(name)");

resolver.createCollector();
verify(delegate).createCollector();
verifyNoMoreInteractions(delegate);
resolver.createCollector();
assertThat(delegate.calls()).containsExactly("createCollector()");

resolver.addProperty(collector, "name", value);
verify(delegate).addProperty(collector, "name", value);
verifyNoMoreInteractions(delegate);
resolver.addProperty(collector, "name", value);
assertThat(delegate.calls()).containsExactly("addProperty(" + collector + ", name, " + value + ")");

resolver.addContent(collector, "text");
verify(delegate).addContent(collector, "text");
verifyNoMoreInteractions(delegate);
resolver.addContent(collector, "text");
assertThat(delegate.calls()).containsExactly("addContent(" + collector + ", text)");

resolver.complete(collector);
verify(delegate).complete(collector);
verifyNoMoreInteractions(delegate);
resolver.complete(collector);
assertThat(delegate.calls()).containsExactly("complete(" + collector + ")");

resolver.parseContent();
verify(delegate).parseContent();
verifyNoMoreInteractions(delegate);
}
resolver.parseContent();
assertThat(delegate.calls()).containsExactly("parseContent()");
}

private static class MockResolver extends ValueResolver {
private List<String> calls = new ArrayList<>();

@Override
public void doNotParseContent() {
calls.add("doNotParseContent()");
super.doNotParseContent();
}

@Override
public ValueResolver resolve(String name) {
calls.add("resolve(%s)".formatted(name));
return super.resolve(name);
}

@Override
public Object createCollector() {
calls.add("createCollector()");
return super.createCollector();
}

@Override
public Object addProperty(Object collector, String name, Object value) {
calls.add("addProperty(%s, %s, %s)".formatted(collector, name, value));
return collector;
}

@Override
public Object addContent(Object collector, String content) {
calls.add("addContent(%s, %s)".formatted(collector, content));
return collector;
}

@Override
public Object complete(Object collector) {
calls.add("complete(%s)".formatted(collector));
return super.complete(collector);
}

@Override
public boolean parseContent() {
calls.add("parseContent()");
return super.parseContent();
}

private List<String> calls() {
List<String> result = calls;
calls = new ArrayList<>();
return result;
}
}
}
Loading

0 comments on commit 8571eb3

Please sign in to comment.