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.
import static org.assertj.core.api.Assertions.assertThat;import static org.assertj.core.api.Assertions.assertThatThrownBy; for exception testsimport static org.assertj.core.api.Assertions.assertThatCode; when verifying no exception is thrownimport 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 APIAssertionsForClassTypes unless there is a genuine generic ambiguity (e.g. invoke() return values). Prefer Assertions first.org.junit.jupiter.api.*) for test lifecycle only — @Test, @BeforeEach, @AfterEach, @TempDir, etc. Use AssertJ for assertions, exceptions, and assumptions.@TempDirUse 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;
@TempDir fields private by default — not package-private or public.java.nio.file.Path on the test class; expose a protected accessor (e.g. getTempFolder()) when subclasses need the directory.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;
@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 {
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());
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.
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.
.hasMessage(...), .hasMessageContaining(...), or .hasMessageStartingWith(...) when the exception has a non-null message..hasMessage(...) when the full message is stable and known (copy it from the throw site or a failing test)..hasMessageContaining(...) when the message includes variable detail (type names, field paths, etc.) or when several loop iterations share a common substring.hasMessage only when the JDK exception message is reliably null (e.g. EOFException, InvalidMarkException, ReadOnlyBufferException, re-thrown checked exceptions with no message)..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.as("description") on assertThatThrownBy chains. Rely on the test method name or inline the intent in the lambda..as(DECIMAL), .as(logicalType), etc. on builder chains inside the lambda — those are not AssertJ descriptions.() -> { … }) 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("…");
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();
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.
// Wrong assertThat(output.exists()).isTrue(); assertThat(outputFile.length()).isEqualTo(0); // Correct assertThat(output).exists(); assertThat(outputFile.length()).isZero(); assertThat(avroFile.length()).isPositive();
toString() comparisonsUse 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());
Prefer AssertJ collection assertions on the collection itself. Assert on the collection (list, conversions, records, encodings, …), not on size(), isEmpty(), or contains() return values.
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());
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.
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.
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).PrimitiveIterator.OfLong, containsExactly expects Long varargs — use L suffixes on literals (1L, 2L, …).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.
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.StreamAssertJ has no StreamAssert — collect 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).
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.
isTrue() / isFalse() is still fineUse 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);
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 intWhen 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.
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.
// 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);
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();
| 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 |
After writing or changing tests, apply formatting and run tests for the affected module:
./mvnw spotless:apply -pl <module> mvn -pl <module> test