Skip to content

Latest commit

 

History

History
697 lines (519 loc) · 28.7 KB

File metadata and controls

697 lines (519 loc) · 28.7 KB

Writing tests (JUnit 5 + AssertJ)

Conventions for writing new tests and reviewing new/changed tests in this repository using AssertJ. Use JUnit 5 (org.junit.jupiter.api.*) for test lifecycle and AssertJ for all assertions, exceptions, and assumptions.

Migrating an existing JUnit 4 test to this style? Start with migrating-tests.md — it covers the mechanical first pass, the JUnit → AssertJ mapping table, and the post-migration audit. The conventions below define the target style those migrations aim for.

Imports

  • Use import static org.assertj.core.api.Assertions.assertThat;
  • Use import static org.assertj.core.api.Assertions.assertThatThrownBy; for exception tests
  • Use import static org.assertj.core.api.Assertions.assertThatCode; when verifying no exception is thrown
  • Use import static org.assertj.core.api.Assumptions.assumeThat; for conditional test execution (skip when assumption fails) — not JUnit Assume, JUnit 5 Assumptions, or any other assumption API
  • Do not import from AssertionsForClassTypes unless there is a genuine generic ambiguity (e.g. invoke() return values). Prefer Assertions first.
  • Use JUnit 5 (org.junit.jupiter.api.*) for test lifecycle only — @Test, @BeforeEach, @AfterEach, @TempDir, etc. Use AssertJ for assertions, exceptions, and assumptions.

@TempDir

Use JUnit 5 @TempDir instead of JUnit 4 TemporaryFolder / @Rule:

import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path;

@TempDir
private Path tempDir;
  • Declare @TempDir fields private by default — not package-private or public.
  • Prefer java.nio.file.Path on the test class; expose a protected accessor (e.g. getTempFolder()) when subclasses need the directory.
  • When both java.nio.file.Path and org.apache.hadoop.fs.Path are in scope, declare @TempDir as private java.nio.file.Path tempDir and do not import java.nio.file.Path if it would clash with Hadoop Path.
// Wrong — package-private or public
@TempDir
Path tempDir;

// Wrong — JUnit 4
@Rule
public TemporaryFolder temp = new TemporaryFolder();

// Correct
@TempDir
private Path tempDir;

// Correct when Hadoop Path is also used
@TempDir
private java.nio.file.Path tempDir;

Path handling (@TempDir → Hadoop Path / File)

Stay on java.nio.file.Path for temp-dir work. Convert to Hadoop or java.io.File only at API boundaries.

Hadoop Path from @TempDir:

// Correct — child path for a writer/reader
Path output = new Path(tempDir.resolve("out.parquet").toUri());

// Correct — unique file that must not exist yet (CREATE mode)
Path output = new Path(tempDir.resolve(UUID.randomUUID().toString()).toUri());

// Correct — directory root
Path root = new Path(tempDir.toUri());

Prefer .toUri() when constructing Hadoop Path from java.nio.file.Path — do not round-trip through toFile().getAbsolutePath() or toString() on an intermediate variable unless the API requires a String.

java.io.File when required:

File file = tempDir.resolve("child.parquet").toFile();

Use tempDir.resolve("name"), not new File(tempDir.toFile(), "name").

Do not pre-create files for Hadoop CREATE-mode writers. Parquet/Hadoop writers open with create-if-not-exists semantics and fail with FileAlreadyExistsException when the path already exists. A UUID (or other unique name) under @TempDir is enough — no Files.createFile / temp.newFile() + delete dance.

// Wrong — creates then deletes, or leaves a file that breaks CREATE
File temp = Files.createTempFile(tempDir, "test", ".tmp").toFile();
Path path = new Path(temp.getAbsolutePath());

java.nio.file.Path tempFile = Files.createFile(tempDir.resolve(UUID.randomUUID().toString()));
Files.delete(tempFile);
Path path = new Path(tempFile.toString());

// Wrong — redundant conversion
new Path(tempDir.toFile().getAbsolutePath())
new Path(new File(tempDir.toFile(), "out").getAbsolutePath())

// Correct — path does not exist yet; writer creates it
Path path = new Path(tempDir.resolve(UUID.randomUUID().toString()).toUri());

Files.createTempFile / Files.createTempDirectory: keep only when the test needs JDK temp-name generation inside @TempDir (e.g. a specific prefix/suffix). If the created path is passed to a CREATE-mode writer, delete it first with Files.delete(path). Prefer tempDir.resolve(...) or Files.createTempDirectory(tempDir, "prefix") + new Path(dir.toUri()) over .toFile().getAbsolutePath().

Shared test bases: use org.apache.parquet.DirectWriterTest from the parquet-hadoop test-jar for direct RecordConsumer writing — do not duplicate it in other modules (e.g. no module-local AvroDirectWriterTest).

import org.apache.parquet.DirectWriterTest;

public class TestArrayCompatibility extends DirectWriterTest {

Actual vs expected order

AssertJ uses actual first, expected second:

// Correct
assertThat(stream.read()).isEqualTo(124);
assertThat(column("a").compareTo(column("b"))).isEqualTo(-1);
assertThat(cw.getEncoding()).isEqualTo(PLAIN_DICTIONARY);

// Wrong (flipped)
assertThat(124).isEqualTo(stream.read());
assertThat(PLAIN_DICTIONARY).isEqualTo(cw.getEncoding());

Assumptions

Use AssertJ assumeThat (import static org.assertj.core.api.Assumptions.assumeThat) to skip tests when a precondition is not met (OS-specific tests, optional features, etc.). Do not use JUnit 4 Assume, JUnit 5 org.junit.jupiter.api.Assumptions, or any JUnit assumption helper.

Prefer fluent assertions on the value under test over wrapping a boolean expression in .isTrue():

// Wrong — JUnit 4
Assume.assumeTrue(System.getProperty("os.name").toLowerCase().startsWith("win"));

// Wrong — JUnit 5
Assumptions.assumeTrue(System.getProperty("os.name").toLowerCase().startsWith("win"));

// Wrong — JUnit 5 static import of Assumptions (still JUnit, not AssertJ)
import static org.junit.jupiter.api.Assumptions.*;
assumeTrue(featureEnabled);

// Correct — AssertJ assumeThat
assumeThat(featureEnabled).isTrue();

// Correct — fluent check on the subject
assumeThat(System.getProperty("os.name").toLowerCase()).startsWith("win");

When an assumption fails, AssertJ throws org.opentest4j.TestAbortedException (same as JUnit 5), so the test is reported as skipped.

Exception assertions

Use assertThatThrownBy for expected exceptions:

assertThatThrownBy(() -> action())
    .isInstanceOf(SomeException.class)
    .hasMessage("exact message");

Every assertThatThrownBy chain must include .isInstanceOf(...) and a message assertion (.hasMessage, .hasMessageContaining, or .hasMessageStartingWith) unless the thrown exception reliably has a null message (see below).

Always use assertThatThrownBy — not catchThrowable followed by assertThat(caught):

// Wrong — split capture and assertion; does not fail clearly when no exception is thrown
Throwable caught = catchThrowable(() -> action());
assertThat(caught).isInstanceOf(BadConfigurationException.class);
assertThat(caught).hasMessageContaining("23");

// Correct
assertThatThrownBy(() -> action())
    .isInstanceOf(BadConfigurationException.class)
    .hasMessageContaining("23");

Use .satisfies(…), .hasSuppressedException(…), .hasNoSuppressedExceptions(), or .isSameAs(…) on the assertThatThrownBy chain when additional checks on the thrown instance are needed. Reserve catchThrowable only when the test must continue after capturing (e.g. multiple independent actions in one test) — not for a single expected failure.

isInstanceOf alone is not sufficient — it loses coverage of the error text users and operators actually see.

Message checks

  • Always add .hasMessage(...), .hasMessageContaining(...), or .hasMessageStartingWith(...) when the exception has a non-null message.
  • Use .hasMessage(...) when the full message is stable and known (copy it from the throw site or a failing test).
  • Use .hasMessageContaining(...) when the message includes variable detail (type names, field paths, etc.) or when several loop iterations share a common substring.
  • Omit hasMessage only when the JDK exception message is reliably null (e.g. EOFException, InvalidMarkException, ReadOnlyBufferException, re-thrown checked exceptions with no message).
  • Never use .hasMessage(null) or .hasMessage((String) null).
// Wrong — type only
assertThatThrownBy(() -> new AvroSchemaConverter().convert(parquetSchemaWithInt96))
    .isInstanceOf(IllegalArgumentException.class);

// Correct
assertThatThrownBy(() -> new AvroSchemaConverter().convert(parquetSchemaWithInt96))
    .isInstanceOf(IllegalArgumentException.class)
    .hasMessage(
        "INT96 is deprecated. As interim enable READ_INT96_AS_FIXED flag to read as byte array.");

// Correct — shared substring across similar failures
assertThatThrownBy(() -> new AvroSchemaConverter().convert(message(type)))
    .isInstanceOf(IllegalArgumentException.class)
    .hasMessageContaining("Cannot annotate schema");

assertThatThrownBy style

  • Do not put .as("description") on assertThatThrownBy chains. Rely on the test method name or inline the intent in the lambda.
  • Do keep .as(DECIMAL), .as(logicalType), etc. on builder chains inside the lambda — those are not AssertJ descriptions.
  • Use an expression lambda for a single action. Reserve block lambdas (() -> { … }) for multiple statements.
// Wrong — unnecessary block for one call
assertThatThrownBy(() -> {
      evaluate(neverCalled);
    })
    .isInstanceOf(ShortCircuitException.class)
    .hasMessage("…");

// Correct — expression lambda (or method reference when it fits)
assertThatThrownBy(() -> evaluate(neverCalled))
    .isInstanceOf(ShortCircuitException.class)
    .hasMessage("…");
assertThatThrownBy(command::run)
    .isInstanceOf(FileAlreadyExistsException.class)
    .hasMessageContaining("File already exists");

// Block lambda is fine when setup is required
assertThatThrownBy(() -> {
      MessageType incompatible = new MessageType("schema", …);
      readGroups(store, originalSchema, incompatible, 1);
    })
    .isInstanceOf(ParquetDecodingException.class)
    .hasMessage("…");
// Wrong
assertThatThrownBy(() -> builder.build())
    .as("Should reject invalid type")
    .isInstanceOf(IllegalArgumentException.class);

// Correct
assertThatThrownBy(() -> builder.build())
    .isInstanceOf(IllegalArgumentException.class)
    .hasMessage("…");

// Builder .as() inside lambda is fine
assertThatThrownBy(() -> Types.required(INT32).as(DECIMAL).named("x"))
    .isInstanceOf(IllegalArgumentException.class)
    .hasMessage("…");

No exception expected

Replace no-op assertTrue(true) after a successful call, try/catch/Assert.fail blocks, or Java assert statements with:

assertThatCode(() -> uuidConverter.addBinary(binary)).doesNotThrowAnyException();
assertThatCode(() -> ToolRunner.run(conf, new Main(logger), args)).doesNotThrowAnyException();

Numeric results

Prefer dedicated numeric assertions over boolean comparisons:

// Wrong
assertThat(command.run()).isEqualTo(0);
assertThat(0 < file.length()).isTrue();
assertThat(columnSizeInBytes.get("DocId") > columnSizeInBytes.get("Num")).isTrue();

// Correct
assertThat(command.run()).isZero();
assertThat(file.length()).isPositive();
assertThat(columnSizeInBytes.get("DocId")).isGreaterThan(columnSizeInBytes.get("Num"));

Use .isZero(), .isPositive(), .isNegative(), .isGreaterThan(...), etc. on the value under test.

Files

// Wrong
assertThat(output.exists()).isTrue();
assertThat(outputFile.length()).isEqualTo(0);

// Correct
assertThat(output).exists();
assertThat(outputFile.length()).isZero();
assertThat(avroFile.length()).isPositive();

String / toString() comparisons

Use AssertJ's .asString() on the subject, not .toString() in assertThat(...):

// Wrong
assertThat(schema.toString()).isEqualTo(expectedMT.toString());
assertThat(stats.toString()).isEqualTo("min: 1.0, max: 2.0, num_nulls: 0");

// Correct — object compared to object (use .toString() on expected to avoid format overload)
assertThat(schema).asString().isEqualTo(expectedMT.toString());

// Correct — object compared to string literal
assertThat(stats).asString().isEqualTo("min: 1.0, max: 2.0, num_nulls: 0");
assertThat(pred).asString().isEqualTo("or(and(not(…");
assertThat(schemaString).contains("\"name\" : \"timestamp_1\"");
assertThat(schemaString).doesNotContain("\"type\" : [ \"null\", \"INT96\" ]");

// Correct — builder chain
assertThat(Types.required(FIXED_LEN_BYTE_ARRAY).length(16).as(uuidType()).named("uuid_field"))
    .asString()
    .isEqualTo("required fixed_len_byte_array(16) uuid_field (UUID)");

Important: asString().isEqualTo(otherObject) can hit AssertJ's isEqualTo(String format, Object...) overload. When comparing to another object, use .isEqualTo(other.toString()) on the expected side.

.as("description") before .asString() is allowed on normal assertions:

assertThat(records.get(0))
    .as("deserialization does not display the same result")
    .asString()
    .isEqualTo(r1.toString());

Collections and sizes

Prefer AssertJ collection assertions on the collection itself. Assert on the collection (list, conversions, records, encodings, …), not on size(), isEmpty(), or contains() return values.

Size

Always use hasSize(n) instead of assertThat(collection.size()).isEqualTo(n) or assertThat(array.length).isEqualTo(n).

When comparing two collections (or a collection and an array) that should have the same length, prefer hasSameSizeAs over hasSize(other.size()) or hasSize(array.length):

// Wrong
assertThat(splits.size()).isEqualTo(offsets.length);
assertThat(splits).hasSize(sizes.length);
assertThat(actual.size()).isEqualTo(expected.size());

// Correct
assertThat(splits).hasSameSizeAs(offsets);
assertThat(splits).hasSameSizeAs(sizes);
assertThat(actual).hasSameSizeAs(expected);

Use hasSizeLessThan / hasSizeGreaterThan when asserting a relative size bound, not a boolean comparison on sizes:

// Wrong
assertThat(result.size() < DATA.size()).isTrue();

// Correct
assertThat(result).hasSizeLessThan(DATA.size());

Membership, emptiness, and equality

Do not wrap collection state in a boolean and assert with isTrue() / isFalse():

// Wrong
assertThat(column.getEncodings().contains(Encoding.PLAIN_DICTIONARY))
    .as("Column should be dictionary encoded: " + name)
    .isTrue();
assertThat(column.getEncodings().contains(Encoding.PLAIN))
    .as("Column should not have plain data pages" + name)
    .isFalse();
assertThat(list).isEqualTo(List.of(1, 2, 3));
assertThat(list).isEqualTo(Collections.emptyList());
assertThat(list.size()).isEqualTo(3);
assertThat(list.isEmpty()).isTrue();
assertThat(!list.isEmpty()).isFalse();
assertThat(list.contains(item)).isTrue();
assertThat(metadata.getBlocks().isEmpty()).isFalse();
assertThat(metadata.getBlocks().size() > 0).isTrue();

// Correct
assertThat(column.getEncodings())
    .as("Column should be dictionary encoded: " + name)
    .contains(Encoding.PLAIN_DICTIONARY);
assertThat(column.getEncodings())
    .as("Column should not have plain data pages" + name)
    .doesNotContain(Encoding.PLAIN);
assertThat(list).containsExactly(1, 2, 3);
assertThat(list).isEmpty();
assertThat(list).isNotEmpty();
assertThat(list).hasSize(3);
assertThat(list).contains(item);
assertThat(metadata.getBlocks()).isNotEmpty();

Use containsExactly for ordered list equality with literal elements. Use containsExactlyElementsOf when comparing to another iterable. Use containsExactlyInAnyOrder only when order does not matter (e.g. HashSet-backed iterables).

Exception: assertThat(stream.size()).isEqualTo(n) on BytesInput / stream-like types is fine — hasSize() applies to collections, not arbitrary size() methods.

Exception: Statistics.isEmpty(), hasNonNullValue(), and similar domain boolean methods are not Java collections — keep assertThat(stats.isEmpty()).isFalse() or assert the specific stat fields directly.

Optional (java.util.Optional)

Assert on the Optional, not on optional.isPresent() / optional.isEmpty() wrapped in isTrue() / isFalse():

// Wrong
assertThat(page.getCrc().isPresent()).as("Checksum was not set in page").isTrue();
assertThat(page.getCrc().isPresent()).as("Checksum was set in page").isFalse();
assertThat(offsetIndex.getUnencodedByteArrayDataBytes(0).isPresent()).isFalse();

// Correct
assertThat(page.getCrc()).as("Checksum was not set in page").isPresent();
assertThat(page.getCrc()).as("Checksum was set in page").isEmpty();
assertThat(offsetIndex.getUnencodedByteArrayDataBytes(0)).isEmpty();

For a present value, prefer assertThat(opt).contains(value) or assertThat(opt).get().isEqualTo(value) over assertThat(opt.isPresent()).isTrue() followed by opt.get().

Exception: Statistics.isEmpty() and other domain boolean methods are not Optional — see Collections above.

Streams and iterators

Iterator content

Replace custom helpers that drain an iterator into a list/array and compare with isEqualTo (e.g. assertIteratorEquals, assertAllRowsEqual). Use AssertJ's IteratorAssert.toIterable() to consume the iterator and apply iterable assertions:

// Wrong — custom helper draining the iterator
static void assertIteratorEquals(PrimitiveIterator.OfInt actualIt, int... expectedValues) {
  IntList actualList = new IntArrayList();
  actualIt.forEachRemaining(actualList::add);
  assertThat(actualList.toIntArray()).isEqualTo(expectedValues);
}
assertIteratorEquals(IndexIterator.all(10), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

// Correct — fluent AssertJ on the iterator
assertThat(IndexIterator.all(10)).toIterable().containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
assertThat(IndexIterator.intersection(lhs, rhs)).toIterable().isEmpty();
assertThat(ranges.iterator()).toIterable().containsExactly(1L, 2L, 3L, 4L);

Notes:

  • PrimitiveIterator.OfInt / OfLong extend Iterator, so assertThat(iterator) works directly.
  • .toIterable() consumes the iterator (same as a drain helper). Non-consuming checks use hasNext() / isExhausted() on the iterator itself (see below).
  • For PrimitiveIterator.OfLong, containsExactly expects Long varargs — use L suffixes on literals (1L, 2L, …).
  • For int... expected values passed as a varargs parameter, box before spreading into containsExactly:
assertThat(predicate.accept(ci))
    .toIterable()
    .containsExactly(Arrays.stream(expectedIndexes).boxed().toArray(Integer[]::new));

Do not add a custom AssertJ assertion class unless the same pattern is needed across many modules — toIterable() is sufficient.

Iterator state (hasNext / isExhausted)

Assert on the iterator, not on iterator.hasNext() wrapped in isTrue() / isFalse():

// Wrong
assertThat(expIt.hasNext()).isFalse();
assertThat(expIt.hasNext()).isTrue();

// Correct
assertThat(expIt).isExhausted();
assertThat(expIt).hasNext();

Use .isExhausted() when all elements were consumed (e.g. after a partial match loop). Use .hasNext() when more elements are expected. .as("…") before these is allowed when the failure message needs context.

java.util.stream.Stream

AssertJ has no StreamAssertcollect first, then assert on the resulting collection:

// Wrong — boolean wrapper on stream terminal operation
assertThat(result.size() == expected.size()).isTrue();

// Wrong — isEqualTo on two independently collected lists (often fine, but prefer iterable API)
assertThat(result).isEqualTo(DATA.stream().filter(pred).collect(Collectors.toList()));

// Correct — collect actual, then compare as iterable
List<User> result = readFilteredUsers(filter);
assertThat(result).containsExactlyElementsOf(DATA.stream().filter(expectedFilter).collect(Collectors.toList()));

// Correct — known expected elements
assertThat(result).containsExactly(userA, userB);

For a Stream used only as an expected source in a helper, passing stream.iterator() to assertThat(it).toIterable() is fine when the stream is consumed once.

When asserting a Java array length, prefer assertThat(array).hasSize(n) over assertThat(array.length).isEqualTo(n).

Maps and sets — combine related checks

Prefer one assertion when size and membership are asserted together:

// Wrong — three assertions for the same map
assertThat(map).hasSize(2);
assertThat(map).containsKey(keyA);
assertThat(map).containsKey(keyB);

// Correct
assertThat(map).containsOnlyKeys(keyA, keyB);

// Wrong — size + contains on a set
assertThat(set).hasSize(2);
assertThat(set).contains("hello", "world");

// Correct
assertThat(set).containsExactlyInAnyOrder("hello", "world");

Use containsOnlyKeys when the map must have exactly those keys. Use containsExactlyInAnyOrder for sets (order irrelevant). Use containsExactly when order matters.

When isTrue() / isFalse() is still fine

Use isTrue() / isFalse() for genuine boolean expressions that are not collection, string, type, reference equality, or membership checks, e.g. filter.keep(path) or method calls that return boolean without a dedicated AssertJ assertion.

Do not use assertThat(x instanceof Foo.class).isTrue() — use assertThat(x).isInstanceOf(Foo.class) instead. For negation, use isNotInstanceOf(...).

Do not use assertThat(a == b).isTrue() for reference equality — use assertThat(a).isSameAs(b) instead.

Do not use assertThat(x == a || x == b).isTrue() for “one of these values” — use assertThat(x).isIn(a, b) instead (or isNotIn(...) for negation).

// Wrong
assertThat(records.get(0).get("dec") instanceof BigDecimal).isTrue();
assertThat(obj instanceof Map).as("Should be a map").isTrue();
assertThat(car.getDoors() == 4 || car.getDoors() == 5).isTrue();

// Correct
assertThat(records.get(0).get("dec")).isInstanceOf(BigDecimal.class);
assertThat(obj).as("Should be a map").isInstanceOf(Map.class);
assertThat(car.getDoors()).isIn(4, 5);

Ordering and comparison

Comparable / compareTo

// Wrong
assertThat(a.compareTo(b) < 0).isTrue();
assertThat(a.compareTo(b) == 0).isTrue();

// Correct
assertThat(a).isLessThan(b);
assertThat(a).isGreaterThan(b);
assertThat(a).isEqualByComparingTo(b);

compare() returning int

When the API returns int (not Comparable on the subject), assert on the result:

assertThat(Float16.compare(a, b)).isZero();
assertThat(Float16.compare(a, b)).isPositive();
assertThat(Float16.compare(a, b)).isNegative();

For custom Comparator instances comparing values directly:

assertThat(truncated)
    .usingComparator(comparator)
    .isLessThanOrEqualTo(value);

Do not use usingComparator with Float16::compare on boxed Short values — it does not match Float16.compare(short, short) semantics.

Reference equality (isSameAs)

The value under test is the AssertJ subject; the known reference is the argument:

// Correct — slice buffer is under test
assertThat(one.array()).as("Should use the same backing array").isSameAs(data.array());

// Correct — dictionary-encoded field should reuse the same object reference
assertThat(car.getModel()).isSameAs(previousCar.getModel());

// Wrong — backing array as subject
assertThat(data.array()).isSameAs(one.array());

// Wrong — reference equality via isTrue()
assertThat(car.model == previousCar.model).isTrue();

.as("…") on non-exception assertions is allowed.

Arrays and bytes

// Prefer
assertThat(bytes).containsExactly(1, 2, 3);
assertThat(bytes).hasSize(12);
assertThat(buffer).isEqualTo(expectedArray);

// Byte values from streams — cast to avoid JUnit-style promotion issues
assertThat((int) stream.read()).isEqualTo(i);
assertThat((byte) buffer.get()).isEqualTo((byte) i);

Booleans and filters

Replace custom match helpers with direct AssertJ:

// Wrong
assertMatches(filter, path);
assertDoesNotMatch(filter, path);

// Correct
assertThat(filter.keep(path)).isTrue();
assertThat(filter.keep(path)).isFalse();

Patterns to avoid (quick reference)

Avoid Use instead
catchThrowable + assertThat(caught).isInstanceOf / hasMessage* assertThatThrownBy(…).isInstanceOf(…).hasMessage*
assertThatThrownBy without hasMessage Add hasMessage / hasMessageContaining from the throw site
@Rule TemporaryFolder / temp.newFile() @TempDir private Path tempDir + tempDir.resolve(...)
tempDir.toFile().getAbsolutePath() / new File(tempDir.toFile(), …) tempDir.resolve(…).toUri() or .toFile()
new Path(tempDir.toFile().getAbsolutePath()) new Path(tempDir.resolve(…).toUri())
Files.createFile + Files.delete before CREATE-mode writer tempDir.resolve(uniqueName) only — file must not exist
Duplicate DirectWriterTest in other modules extends DirectWriterTest from parquet-hadoop test-jar
Assume.assumeTrue(...) / Assumptions.assumeTrue(...) AssertJ assumeThat(...).isTrue() or fluent assumeThat(value).…
singleElement() containsExactly(element) or explicit checks
.satisfies(…) Direct assertions unless truly necessary
assertThat(x.toString()) assertThat(x).asString()
assertThat(list.isEmpty()).isTrue() assertThat(list).isEmpty()
assertThat(list.isEmpty()).isFalse() / assertThat(!list.isEmpty()).isTrue() assertThat(list).isNotEmpty()
assertThat(opt.isPresent()).isTrue() assertThat(opt).isPresent()
assertThat(opt.isPresent()).isFalse() / assertThat(opt.isEmpty()).isTrue() assertThat(opt).isEmpty()
assertThat(list.contains(x)).isTrue() assertThat(list).contains(x)
assertThat(list.contains(x)).isFalse() assertThat(list).doesNotContain(x)
assertThat(list).isEqualTo(otherList) assertThat(list).containsExactlyElementsOf(otherList)
assertThat(list.size()).isEqualTo(n) assertThat(list).hasSize(n)
assertThat(list).hasSize(other.size()) / hasSize(array.length) assertThat(list).hasSameSizeAs(other)
assertThat(a.size() < b.size()).isTrue() assertThat(a).hasSizeLessThan(b)
assertThat(array.length).isEqualTo(n) assertThat(array).hasSize(n)
assertThat(it).toIterable() for drained iterators assertThat(it).toIterable().containsExactly(1, 2, 3)
assertThat(it.hasNext()).isFalse() assertThat(it).isExhausted()
assertThat(it.hasNext()).isTrue() assertThat(it).hasNext()
assertThat(list).isEqualTo(stream.collect(toList())) assertThat(list).containsExactlyElementsOf(stream.collect(toList()))
hasSize(n) + containsKey / contains on same map or set containsOnlyKeys(…) / containsExactlyInAnyOrder(…)
assertThat(x instanceof Foo).isTrue() assertThat(x).isInstanceOf(Foo.class)
assertThat(a == b).isTrue() assertThat(a).isSameAs(b)
assertThat(x == a || x == b).isTrue() assertThat(x).isIn(a, b)
assertThat(x).isEqualTo(0) on int/long results assertThat(x).isZero()
assertThat(0 < x).isTrue() assertThat(x).isPositive()
assertThat(file.exists()).isTrue() assertThat(file).exists()
assertThat(true).isTrue() / try-catch for no exception assertThatCode(() -> …).doesNotThrowAnyException()
.as() on assertThatThrownBy (omit)
assertThatThrownBy(() -> { action(); }) (single statement) assertThatThrownBy(() -> action()) or method reference
hasMessage(null) (omit hasMessage)
Constant as assertion subject Expression under test as subject
hasCauseInstanceOf when exception is thrown directly Use isInstanceOf on the thrown type

Verification

After writing or changing tests, apply formatting and run tests for the affected module:

./mvnw spotless:apply -pl <module>
mvn -pl <module> test