JUnit Assertions: assertEquals, assertAll and More (JUnit 5)

JUnit assertions explained with examples for JUnit 5 and 6, from assertEquals and assertAll to assertLinesMatch, assertInstanceOf, timeouts and fail().

Six groups of JUnit Assertions methods: values (assertEquals, assertNotEquals, assertSame, assertNotSame), conditions and nulls (assertTrue, assertFalse, assertNull, assertNotNull), collections and text (assertArrayEquals, assertIterableEquals, assertLinesMatch, assertInstanceOf), exceptions (assertThrows, assertThrowsExactly, assertDoesNotThrow), time limits (assertTimeout, assertTimeoutPreemptively), grouping and failing (assertAll, fail)

JUnit assertions are the static methods of the org.junit.jupiter.api.Assertions class that compare the actual result of the code under test with the expected result and fail the test with an AssertionFailedError when they differ. The same class exists in JUnit 5 and JUnit 6, so the examples run on both. Every JUnit test ends with one or more assertions, because a test without them passes even when the code returns a wrong value.

We use assertions to check return values, object state, collections, exceptions and execution time. The following example tests a small Invoice class of a billing app and uses one line per assertion type, each with its result as a comment.

assertEquals(2, invoice.itemCount());                                   // passes
assertEquals(12.3, invoice.total(), 0.001);                             // passes, 12.3 within 0.001
assertNotEquals(0, invoice.itemCount());                                // passes
assertTrue(invoice.total() > 10);                                       // passes
assertFalse(invoice.isEmpty());                                         // passes
assertNull(invoice.discountCode());                                     // passes, no discount yet
assertNotNull(invoice.customer());                                      // passes
assertSame(service.get("2026-002"), service.get("2026-002"));           // passes, same cached object
assertArrayEquals(new int[] {3, 2}, invoice.quantities());              // passes
assertIterableEquals(List.of("pen", "notebook"), invoice.itemNames());  // passes
Card card = assertInstanceOf(Card.class, service.paymentMethodFor("card"));   // passes, returns the Card
assertAll("invoice",
    () -> assertEquals("2026-001", invoice.number()),
    () -> assertEquals("Lokesh", invoice.customer().name()));          // passes, both checks ran
assertThrows(IllegalArgumentException.class, () -> invoice.addItem("pen", 0, 1.10));   // passes
String file = assertTimeout(Duration.ofSeconds(1), () -> service.exportPdf(invoice));    // 2026-001.pdf

Notice that the expected value always comes first and the actual value second, and that some assertions return a value we can keep using, such as the cast Card or the exported file name. We go through each group of methods with its failure output, compare the JUnit methods with AssertJ at the end and answer the common questions.

1. The Assertions Class in JUnit 5 and JUnit 6

All Jupiter assertions live in one class, org.junit.jupiter.api.Assertions, and we import its methods statically, such as import static org.junit.jupiter.api.Assertions.assertEquals;. When an assertion fails, it throws an AssertionFailedError from the opentest4j library, and JUnit marks the test as failed. The IDE and Maven read the expected and actual values from that error and show them side by side.

Every snippet runs on JUnit 6.1.3 with Java 25 and Maven 3.9 or later. JUnit 6 needs Java 17, while JUnit 5 runs on Java 8, but the Assertions class has the same methods and overloads in both, so readers on JUnit 5.11 or later can copy every snippet. The tests are in the junit-assertions project on GitHub. The JUnit Maven dependency guide covers the project setup, and the JUnit tutorial lists the other JUnit guides.

Six groups of JUnit Assertions methods: values (assertEquals, assertNotEquals, assertSame, assertNotSame), conditions and nulls (assertTrue, assertFalse, assertNull, assertNotNull), collections and text (assertArrayEquals, assertIterableEquals, assertLinesMatch, assertInstanceOf), exceptions (assertThrows, assertThrowsExactly, assertDoesNotThrow), time limits (assertTimeout, assertTimeoutPreemptively), grouping and failing (assertAll, fail)
The Assertions class groups into six kinds of checks, and each failed check throws AssertionFailedError.

Almost every method has three overloads that differ only in the last parameter, the failure message. The message is either a String or a Supplier<String>, and JUnit calls the supplier only when the assertion fails, so an expensive message costs nothing in a passing test.

assertEquals(2, invoice.itemCount(), "item count");
assertEquals(2, invoice.itemCount(), () -> "item count of " + invoice.print());

The message is the last argument in JUnit 5 and JUnit 6, whereas JUnit 4’s Assert class takes it first. A migrated assertEquals(“customer name”, “Lokesh”, name) still compiles in Jupiter when all three arguments are strings, but it compares the message with the expected name.

MethodPasses whenReturns
assertEquals(expected, actual)both are equal by equals(), or within a delta for float and doublenothing
assertNotEquals(unexpected, actual)the values differnothing
assertTrue(condition) / assertFalse(condition)the boolean or BooleanSupplier is true / falsenothing
assertNull(actual) / assertNotNull(actual)the value is / is not nullnothing
assertSame(expected, actual) / assertNotSame()both references point / do not point to one objectnothing
assertArrayEquals(expected, actual)arrays have equal elements in the same ordernothing
assertIterableEquals(expected, actual)iterables are deeply equal, element by elementnothing
assertLinesMatch(expected, actual)each line is equal, matches a regex or is skipped by a fast-forward markernothing
assertInstanceOf(type, actual)the value is an instance of the typethe value, cast to the type
assertAll(executables…)every executable passes; all failures are reported togethernothing
assertThrows(type, executable)the code throws the type or a subclassthe exception
assertDoesNotThrow(executable)the code throws nothingthe supplier’s result
assertTimeout(duration, executable)the code finishes in time, measured after it endsthe supplier’s result
assertTimeoutPreemptively(duration, executable)the code finishes in time; it is stopped when the time is upthe supplier’s result
fail(message)neverdeclared generic, so it fits any expression

2. Comparing Values With assertEquals() and assertNotEquals()

The assertEquals() method compares the expected and the actual value. For objects it calls expected.equals(actual), and for primitives it compares the values, with overloads for byte, short, int, long, char, float and double. The assertNotEquals() method has the same primitive and Object overloads and passes when the values differ.

Floating-point sums rarely hit an exact value. An invoice with three erasers at 0.10 each has a total of 0.30000000000000004 in double arithmetic, so the float and double overloads take a third argument, the delta, which is the largest difference we accept.

Invoice erasers = new Invoice("2026-003", new Customer("Lokesh", "lokesh@example.com"), LocalDate.of(2026, 10, 11))
    .addItem("eraser", 3, 0.10);
double erasersTotal = erasers.total();                                  // 0.30000000000000004
assertEquals(0.3, erasersTotal, 0.001);                                 // passes within the delta
assertEquals(new Customer("Lokesh", "lokesh@example.com"), invoice.customer());   // passes, records compare fields
assertNotEquals("2026-002", invoice.number());

Without the delta, the same check fails, and the message shows our text in front of both values.

org.opentest4j.AssertionFailedError: invoice total ==> expected: <0.3> but was: <0.30000000000000004>

For money in production code, BigDecimal avoids the rounding issue altogether. The delta overloads are meant for measured or computed values such as averages and percentages.

3. Checking Conditions and Nulls

The assertTrue() and assertFalse() methods check a boolean condition or a BooleanSupplier. The assertNull() and assertNotNull() methods check a reference. In a billing app, a new invoice has no discount code, and the code appears only after applyDiscount().

assertTrue(invoice.total() > 10, "total above 10");
assertTrue(() -> invoice.itemNames().contains("pen"));
assertFalse(invoice.isEmpty());
assertNull(invoice.discountCode());
invoice.applyDiscount("AUTUMN10");
assertNotNull(invoice.discountCode());

A failed assertTrue() only says expected: <true> but was: <false>, so the message argument matters more here than anywhere else. When we compare two values, assertEquals() gives a better report than assertTrue(a == b), because it prints both values.

4. Same Object or Equal Object

The assertSame() method checks that two references point to the same object, using ==, whereas assertEquals() uses equals(). We use assertSame() for caches, singletons and methods that must return this. The InvoiceService caches invoices by number, so two calls return one object, and copyOf() creates a new one.

Invoice first = service.get("2026-002");
Invoice second = service.get("2026-002");
Invoice copy = service.copyOf(first);

assertSame(first, second);                                              // passes, cache returns one object
assertNotSame(first, copy);                                             // passes, copyOf() creates a new object

5. Arrays, Iterables and Lines of Text

Arrays do not override equals(), so assertEquals() on two arrays compares references and fails for equal content. The assertArrayEquals() method compares length and elements, and it has a delta overload for float[] and double[]. The assertIterableEquals() method does the same for any Iterable, and it compares nested iterables deeply, so a list of lists is compared element by element.

assertArrayEquals(new int[] {3, 2}, invoice.quantities());
assertArrayEquals(new double[] {3.3, 9.0}, new double[] {3 * 1.10, 2 * 4.50}, 0.001);
assertIterableEquals(List.of("pen", "notebook"), invoice.itemNames());
assertIterableEquals(List.of(List.of("pen"), List.of("ink")), List.of(List.of("pen"), List.of("ink")));

The failure message names the first index that differs, which saves us from comparing two long lists by eye.

org.opentest4j.AssertionFailedError: iterable contents differ at index [1], expected: <ink> but was: <notebook>

The assertLinesMatch() method compares a List<String> or a Stream<String> line by line, which suits printed reports and log output. For each pair, JUnit applies three rules in order.

  • If the expected line equals the actual line, the pair matches.
  • Otherwise, JUnit treats the expected line as a regular expression and calls String.matches().
  • Otherwise, if the expected line is a fast-forward marker such as >> item lines >>, JUnit skips actual lines until the next expected line matches. The text between the markers is a comment, and a number such as >> 4 >> skips that many lines.

The printed invoice contains a date that changes every day and one line per item, so the test uses a regex for the date and a marker for the items.

List<String> expected = List.of(
    "INVOICE 2026-001",
    "Date: \\d{4}-\\d{2}-\\d{2}",
    ">> item lines >>",
    "Total: 12.30");
assertLinesMatch(expected, invoice.print());
assertLinesMatch(List.of("INVOICE 2026-001", ">> 4 >>", "Total: 12.30"), invoice.print());   // passes, skips 4 lines

When the marker never finds the next expected line, the message says which line was missing.

org.opentest4j.AssertionFailedError: fast-forward(?) didn't find: `Total: 12.00`

6. Type Checks With assertInstanceOf()

The assertInstanceOf() method, added in JUnit 5.8, checks the runtime type of a value and returns it already cast. The payment service returns the sealed type PaymentMethod, and the test needs the Card fields.

PaymentMethod method = service.paymentMethodFor("card");

Card card = assertInstanceOf(Card.class, method);
assertEquals("4242", card.lastDigits());

Without it, we would write assertTrue(method instanceof Card) and a cast on the next line, and the failure would not name the actual type. The same method also checks the cause of an exception.

7. Grouping Assertions With assertAll()

A test stops at the first failed assertion, so a test with five checks reports only the first broken value. The assertAll() method runs every executable it gets, collects the failures and reports them together in a MultipleFailuresError. The first argument is an optional heading for the report.

assertAll("invoice",
    () -> assertEquals("2026-001", invoice.number()),
    () -> assertEquals(2, invoice.itemCount()),
    () -> assertEquals(12.3, invoice.total(), 0.001));

With a wrong number and a wrong item count, one run reports both problems, and the third check, which passes, does not appear.

org.opentest4j.MultipleFailuresError: 
invoice (2 failures)
	org.opentest4j.AssertionFailedError: expected: <2026-002> but was: <2026-001>
	org.opentest4j.AssertionFailedError: expected: <3> but was: <2>

Inside one executable, the lines still run in order and stop at the first failure. We use that for dependent checks. If the customer is null, checking its name makes no sense, so the nested group runs only after assertNotNull() passes, while the item-count check runs in any case.

assertAll("customer",
    () -> {
      Customer customer = invoice.customer();
      assertNotNull(customer);
      assertAll("customer fields",
          () -> assertEquals("Lokesh", customer.name()),
          () -> assertTrue(customer.email().endsWith("@example.com")));
    },
    () -> assertEquals(2, invoice.itemCount()));

8. Exceptions With assertThrows() and assertDoesNotThrow()

The assertThrows() method passes when the code throws the expected type or a subclass, and it returns the exception so we can check the message. The assertDoesNotThrow() method passes when nothing is thrown and returns the value of the lambda.

IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
    () -> invoice.addItem("pen", 0, 1.10));
String message = thrown.getMessage();                                   // quantity must be positive: 0

PaymentMethod method = assertDoesNotThrow(() -> service.paymentMethodFor("bank"));

Subclass matching, assertThrowsExactly(), checked exceptions, causes and the JUnit 4 ExpectedException rule get their own article on JUnit assertThrows().

9. assertTimeout() vs assertTimeoutPreemptively()

Both methods fail when the code takes longer than a Duration, and both return the result of a ThrowingSupplier. They differ in what happens to the code that is too slow.

  • The assertTimeout() method runs the code in the test thread, waits until it ends and compares the elapsed time afterwards. A slow call still runs to the end.
  • The assertTimeoutPreemptively() method runs the code in a separate thread and fails as soon as the time is up. JUnit interrupts that thread, and code that ignores the interrupt keeps running in the background.
String file = assertTimeout(Duration.ofSeconds(1), () -> service.exportPdf(invoice));
String again = assertTimeoutPreemptively(Duration.ofSeconds(1), () -> service.exportPdf(invoice));

With an export that takes 500 ms and a limit of 100 ms, the two failure messages show the difference. The first method waited for the export and reports by how much it was late, and the second stopped waiting after 100 ms.

org.opentest4j.AssertionFailedError: execution exceeded timeout of 100 ms by 401 ms
org.opentest4j.AssertionFailedError: execution timed out after 100 ms
Caused by: org.junit.jupiter.api.timeout.PreemptiveTimeoutUtils$ExecutionTimeoutException: Execution timed out in thread junit-timeout-thread-1

Code inside assertTimeoutPreemptively() runs in another thread, so anything stored in a ThreadLocal is missing there. A Spring test transaction is bound to the test thread, so database changes made inside the block are not rolled back with the test. Use assertTimeout() in such tests.

For a time limit on a whole test method or on all tests of a class, the @Timeout annotation is the better tool, because it needs no lambda and can be set globally.

10. Failing a Test on Purpose With fail()

The fail() method fails the test unconditionally. It has overloads with a message, a Supplier<String>, a cause, or both a message and a cause. Its return type is generic, so we can call it inside an expression that must produce a value, such as orElseGet().

String first = invoice.itemNames().stream()
    .findFirst()
    .orElseGet(() -> fail("invoice has no items"));                    // fail() returns any type

A placeholder test that calls fail() reports its message as the reason. For a test that should not run yet, @Disabled is the cleaner choice, because the build stays green and the report shows the test as skipped.

org.opentest4j.AssertionFailedError: test for credit notes is not written yet

With sealed types and pattern matching, the compiler already checks that a switch covers every case, so we no longer need fail() in a default branch of such a switch.

11. JUnit Assertions vs AssertJ

The JUnit team recommends third-party libraries such as AssertJ, Hamcrest or Truth when a project needs richer checks. AssertJ offers a fluent API that starts with assertThat(actual) and chains checks, so the actual value comes first and the IDE suggests the checks that fit its type.

CheckJUnit AssertionsAssertJ
EqualityassertEquals(2, invoice.itemCount())assertThat(invoice.itemCount()).isEqualTo(2)
List content in any ordernot built inassertThat(names).containsExactlyInAnyOrder(“notebook”, “pen”)
String contentassertTrue(s.startsWith(“INV”))assertThat(s).startsWith(“INV”)
Grouped checksassertAll(…)SoftAssertions.assertSoftly(…)
Extra dependencynoneassertj-core

Both can live in one project. We keep the JUnit methods for simple values and switch to AssertJ when a test checks collections, strings or object graphs in detail.

12. JUnit Assertions FAQs

Beginners mostly ask about the package to import, the kinds of equality and what happens after a failed check.

12.1. Which Package Do JUnit 5 Assertions Come From?

From org.junit.jupiter.api.Assertions, in the junit-jupiter-api artifact. JUnit 6 uses the same package. The JUnit 4 class org.junit.Assert belongs to the old API and should not be mixed into Jupiter tests.

12.2. What Is the Difference Between assertEquals() and assertSame()?

The assertEquals() method compares with equals(), so two different objects with the same content pass. The assertSame() method compares with == and passes only for the same object, as the cache example in section 4 shows.

12.3. How Do We Compare Lists in JUnit?

We use assertEquals(expectedList, actualList) or assertIterableEquals(). Both compare elements in order, and assertIterableEquals() also works for any Iterable and names the first index that differs. For order-independent checks, we compare sets or use AssertJ’s containsExactlyInAnyOrder().

12.4. Does a Test Continue After a Failed Assertion?

No. A failed assertion throws AssertionFailedError, and the rest of the test method does not run. Only the executables inside assertAll() continue after a failure, which is the reason to group independent checks.

12.5. Are JUnit 6 Assertions Different From JUnit 5?

No. The Assertions class has the same methods in both versions. JUnit 6.0 raised the minimum Java version to 17 and added JSpecify nullability annotations to the API, and JUnit 6.1 attaches the checked value as the cause when assertInstanceOf() fails for a Throwable. The JUnit 5 vs JUnit 6 comparison lists the other changes.

13. Conclusion

The Assertions class is the same in JUnit 5 and JUnit 6. We pass the expected value first, the actual value second and an optional message last, and we prefer the method that prints the most useful failure, such as assertEquals() over assertTrue(a == b).

For floating-point results we add a delta, for arrays and lists we use the dedicated methods, and for printed text assertLinesMatch() handles regexes and skipped lines. The assertAll() method reports all failures of a group in one run.

Exceptions, time limits and type checks have their own methods that return the exception, the result or the cast value. When the built-in methods become verbose, AssertJ is the usual next step.

14. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.