Assertions are the heart of every JUnit 6 test. They are the statements that determine whether a test passes or fails. JUnit 6 provides a rich set of assertion methods in the org.junit.jupiter.api.Assertions class — from simple equality checks to grouped assertions, exception verification, and timeout enforcement. This guide covers every assertion method with real examples, console output, and guidance on when to use each one.
All assertion methods are static, so the standard pattern is to use a static import at the top of your test class:
import static org.junit.jupiter.api.Assertions.*;
1. assertEquals and assertNotEquals
The most commonly used assertions. They compare two values for equality using .equals():
@Test
@DisplayName("assertEquals and assertNotEquals examples")
void equalityAssertions() {
String greeting = "Hello, JUnit 6";
// Basic equality check
assertEquals("Hello, JUnit 6", greeting);
// With a custom failure message (shown if assertion fails)
assertEquals("Hello, JUnit 6", greeting, "Greeting string should match exactly");
// Lazy message: the Supplier lambda only evaluates if the assertion FAILS
// Use this when building the message is expensive (e.g. involves I/O)
assertEquals("Hello, JUnit 6", greeting,
() -> "Expected greeting to be 'Hello, JUnit 6' but got: " + greeting);
// assertNotEquals: passes when values are NOT equal
assertNotEquals("Goodbye", greeting, "Greeting should not be 'Goodbye'");
// Floating-point comparison with a delta (tolerance)
// Never compare doubles with == or assertEquals without a delta
double result = 0.1 + 0.2; // = 0.30000000000000004 due to floating-point
assertEquals(0.3, result, 0.0001, "0.1 + 0.2 should be approximately 0.3");
}
2. assertTrue and assertFalse
@Test
@DisplayName("assertTrue and assertFalse examples")
void booleanAssertions() {
List languages = List.of("Java", "Python", "Go");
// assertTrue: passes when condition is true
assertTrue(languages.contains("Java"), "List should contain Java");
assertTrue(languages.size() > 0, "List should not be empty");
// assertFalse: passes when condition is false
assertFalse(languages.isEmpty(), "List should not be empty");
assertFalse(languages.contains("COBOL"), "List should not contain COBOL");
// Works with Predicate-style lambdas for complex conditions
assertTrue(languages.stream().anyMatch(lang -> lang.startsWith("J")),
"At least one language should start with J");
}
3. assertNull and assertNotNull
@Test
@DisplayName("assertNull and assertNotNull examples")
void nullAssertions() {
String uninitialised = null;
String initialised = "JUnit 6";
// assertNull: passes when value IS null
assertNull(uninitialised, "Uninitialised variable should be null");
// assertNotNull: passes when value is NOT null
assertNotNull(initialised, "Initialised variable should not be null");
// Common real-world usage: verify a factory method returns a non-null object
Order order = OrderFactory.createDefault();
assertNotNull(order, "OrderFactory.createDefault() must not return null");
assertNotNull(order.getId(), "Created order must have an assigned ID");
}
4. assertSame and assertNotSame
These check object identity (same reference, using ==), not equality:
@Test
@DisplayName("assertSame vs assertEquals: identity vs equality")
void identityVsEqualityAssertions() {
String original = new String("JUnit");
String copy = new String("JUnit");
String sameRef = original;
// assertEquals: uses .equals() — passes for value equality
assertEquals(original, copy, "Both strings have the same value");
// assertSame: uses == — fails because 'original' and 'copy' are different objects
assertNotSame(original, copy,
"'original' and 'copy' are different String objects in memory");
// assertSame: passes because 'sameRef' points to the exact same object
assertSame(original, sameRef,
"'sameRef' and 'original' reference the same object");
// Real use case: verify a singleton returns the same instance
ConfigService firstCall = ConfigService.getInstance();
ConfigService secondCall = ConfigService.getInstance();
assertSame(firstCall, secondCall, "getInstance() must always return the same singleton");
}
5. assertThrows and assertDoesNotThrow
@Test
@DisplayName("assertThrows: verify exception type and message")
void exceptionAssertions() {
UserService userService = new UserService();
// assertThrows returns the thrown exception for further inspection
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> userService.registerUser(null, "password123"),
"Registering with null email should throw IllegalArgumentException"
);
// Inspect exception message
assertEquals("Email must not be null", exception.getMessage());
// assertDoesNotThrow: confirms happy-path code is exception-free
assertDoesNotThrow(
() -> userService.registerUser("[email protected]", "securePass99"),
"Valid registration should not throw any exception"
);
}
@Test
@DisplayName("assertThrows with checked exceptions")
void checkedExceptionAssertion() {
FileParser parser = new FileParser();
// Works equally well with checked exceptions
IOException ioException = assertThrows(
IOException.class,
() -> parser.parseFile("/nonexistent/path.txt"),
"Parsing a missing file should throw IOException"
);
assertTrue(ioException.getMessage().contains("nonexistent"),
"Exception message should reference the missing path");
}
6. assertAll — Grouped Assertions
assertAll runs ALL its assertions and reports every failure at once. Without it, the first failed assertion stops the test and you miss other failures:
@Test
@DisplayName("assertAll: report all failures in one run")
void groupedAssertions() {
User user = userService.createUser("Alice", "[email protected]", 28);
// Without assertAll: if 'name' check fails, 'email' and 'age' checks never run.
// With assertAll: ALL three run, and ALL failures are reported together.
assertAll("user properties after creation",
() -> assertEquals("Alice", user.getName(), "User name"),
() -> assertEquals("[email protected]", user.getEmail(), "User email"),
() -> assertEquals(28, user.getAge(), "User age"),
() -> assertNotNull( user.getId(), "User ID must be assigned")
);
}
// Output if name and age are both wrong:
// org.opentest4j.MultipleFailuresError: user properties after creation (2 failures)
// [1] expected: but was:
// [2] expected: but was:
7. assertTimeout and assertTimeoutPreemptively
import java.time.Duration;
@Test
@DisplayName("assertTimeout: test completes within time limit")
void timeoutAssertions() throws InterruptedException {
// assertTimeout: runs the executable and fails AFTER it finishes if it exceeded the limit.
// The executable always runs to completion.
assertTimeout(Duration.ofMillis(500),
() -> {
// Simulate some work
Thread.sleep(100);
assertEquals(42, compute());
},
"Computation should complete within 500 ms"
);
// assertTimeoutPreemptively: ABORTS execution if it exceeds the timeout.
// Use when you cannot afford to wait for slow code to finish.
// NOTE: runs in a separate thread — may cause issues with ThreadLocal state.
assertTimeoutPreemptively(Duration.ofMillis(200),
() -> performFastOperation(),
"Fast operation should complete well within 200 ms"
);
}
private int compute() { return 42; }
private void performFastOperation() { /* fast work here */ }
8. assertIterableEquals and assertArrayEquals
@Test
@DisplayName("Collection and array equality assertions")
void collectionAssertions() {
// assertIterableEquals: elements must be equal AND in the same order
List expectedTags = List.of("java", "testing", "junit6");
List actualTags = tagService.getTagsForPost(42);
assertIterableEquals(expectedTags, actualTags,
"Post tags should match expected list in order");
// assertArrayEquals: deep equality for arrays
int[] expectedScores = {95, 87, 76, 100};
int[] actualScores = scoreCalculator.computeScores();
assertArrayEquals(expectedScores, actualScores,
"Computed scores should match expected values");
// assertArrayEquals with delta for double arrays
double[] expectedRatios = {0.25, 0.50, 0.75};
double[] actualRatios = ratioCalculator.compute();
assertArrayEquals(expectedRatios, actualRatios, 0.001,
"Computed ratios should be within 0.001 tolerance");
}
9. assertLinesMatch
@Test
@DisplayName("assertLinesMatch: compare lists of strings with regex support")
void linesMatchAssertion() {
List expectedLines = List.of(
"Starting application...",
">> d+ ms", // regex: matches any line like ">> 42 ms"
"Application ready."
);
List actualLogLines = List.of(
"Starting application...",
">> 137 ms",
"Application ready."
);
// Lines starting with >> are treated as regex patterns
assertLinesMatch(expectedLines, actualLogLines,
"Log output should match expected pattern");
}
10. fail()
@Test
@DisplayName("fail() forces an explicit test failure")
void explicitFailureExample() {
try {
riskyOperation();
// If we reach here, the exception was NOT thrown — that is a failure
fail("Expected RiskyOperationException was not thrown");
} catch (RiskyOperationException expected) {
// Exception was thrown as expected — test passes
assertEquals("Operation failed due to invalid state", expected.getMessage());
}
// Modern alternative: prefer assertThrows over try/catch + fail
}
Quick Reference: All Assertion Methods
| Method | Passes when… | Best used for |
|---|---|---|
assertEquals(a, b) | a.equals(b) | Values, strings, objects |
assertNotEquals(a, b) | !a.equals(b) | Verifying change happened |
assertTrue(cond) | condition is true | Boolean results, predicates |
assertFalse(cond) | condition is false | Negative conditions |
assertNull(obj) | obj is null | Unset fields |
assertNotNull(obj) | obj is not null | Factory/builder output |
assertSame(a, b) | a == b | Singletons, caches |
assertNotSame(a, b) | a != b | Defensive copy checks |
assertThrows(E, exec) | exec throws E | Exception paths |
assertDoesNotThrow(exec) | exec throws nothing | Happy-path validation |
assertAll(execs...) | All pass (all run) | Object state verification |
assertTimeout(d, exec) | exec finishes <= d | Performance bounds |
assertTimeoutPreemptively(d, exec) | exec finishes <= d | Strict time limits |
assertIterableEquals(a, b) | elements equal & ordered | Lists, sets |
assertArrayEquals(a, b) | arrays deeply equal | int[], double[], Object[] |
assertLinesMatch(a, b) | lines match (regex ok) | Log output, text files |
fail(msg) | Never | Unreachable code markers |
Frequently Asked Questions (FAQs)
Q1: What is the correct order of arguments in assertEquals?
The convention is assertEquals(expected, actual) — expected value first, actual value second. This matters for failure messages: JUnit reports "expected: <X> but was: <Y>", which is only readable in the right direction. Swapping them produces confusing messages like "expected: <actual> but was: <expected>".
Q2: When should I use assertAll instead of multiple assertEquals calls?
Use assertAll whenever you are checking multiple properties of the same object and you want to see all failures at once. With chained individual assertions, the first failure stops the test. assertAll runs every lambda, collects all failures, and reports them together — saving you multiple test-fix-rerun cycles.
Q3: What is the difference between assertTimeout and assertTimeoutPreemptively?
assertTimeout lets the executable run to completion and then checks if it exceeded the timeout. assertTimeoutPreemptively runs the executable in a separate thread and aborts it if the timeout is exceeded. Use the preemptive version when you genuinely cannot wait, but be aware it can interact poorly with ThreadLocal state (e.g., Spring’s request context).
Q4: How do I compare floating-point numbers correctly?
Always use assertEquals(expected, actual, delta) for double and float comparisons. Due to IEEE 754 floating-point representation, 0.1 + 0.2 is not exactly 0.3 in Java. A delta of 0.0001 is appropriate for most cases. For financial calculations, use BigDecimal instead and compare with assertEquals or compareTo.
Q5: Can I write custom assertion methods in JUnit 6?
Yes. Create a utility class with static methods that call standard JUnit 6 assertions internally. For example, assertValidOrder(Order order) can internally call assertAll with several assertions. This reduces duplication in tests and keeps assertions close to your domain language. For richer custom assertions, consider the AssertJ library, which integrates seamlessly with JUnit 6.
See Also
- JUnit 6 Tutorial: Complete Series Index
- Writing Your First Clean Test in JUnit 6 (Best Practices)
- JUnit 6 Assumptions and Conditional Test Execution Guide
- Parameterized Tests in JUnit 6: All Sources Explained
- JUnit 6 Test Lifecycle Explained
Conclusion
JUnit 6’s assertion library covers every verification scenario you will encounter in real-world Java testing. For everyday use, assertEquals, assertTrue, assertThrows, and assertAll cover the vast majority of cases. Reach for assertTimeout when performance matters, assertIterableEquals for collections, and assertLinesMatch for log and text output verification.
Next: JUnit 6 Assumptions and Conditional Test Execution — learn how to skip tests gracefully when preconditions are not met, without marking them as failures.