Skip to content

Lesson 01 · JUnit 5 Essentials

Beyond the 1Z0-830 exam

Every lab in this course is a JUnit 5 test. This lesson turns "I can read them" into "I can write them": the lifecycle, assertions, assumptions, and asserting exceptions the JUnit 5 way.

Objectives

After this lesson you will be able to:

  • Write tests with the JUnit 5 (Jupiter) lifecycle annotations.
  • Choose the right assertion and assert that code throws.
  • Use assumptions to skip tests when preconditions aren't met.
  • Name tests readably with @DisplayName.

A first test and the lifecycle

java
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {
    Calculator calc;

    @BeforeAll  static void once()  { /* runs once, before all tests (must be static) */ }
    @BeforeEach void setUp()        { calc = new Calculator(); }  // fresh state per test
    @AfterEach  void tearDown()     { /* runs after each test */ }
    @AfterAll   static void done()  { /* runs once, after all tests (static) */ }

    @Test
    @DisplayName("adds two positive numbers")
    void adds() {
        assertEquals(5, calc.add(2, 3));
    }
}
AnnotationWhenStatic?
@BeforeAll / @AfterAllonce per classyes (unless @TestInstance(PER_CLASS))
@BeforeEach / @AfterEacharound every @Testno
@Testa test caseno

Gotcha

JUnit creates a new test instance per test method by default, so fields are reset between tests — that's deliberate isolation. State you build in @BeforeEach does not leak to the next test. @BeforeAll/@AfterAll must be static because they run before any instance exists.

Assertions

java
assertEquals(expected, actual);              // note order: expected first
assertTrue(list.isEmpty());
assertNull(result);
assertSame(a, b);                            // reference identity, not equals
assertArrayEquals(new int[]{1,2}, arr);
assertEquals(0.3, x, 1e-9);                  // floating point needs a delta
assertAll("user",                            // group — reports ALL failures, not just the first
    () -> assertEquals("Ada", u.name()),
    () -> assertEquals(36, u.age()));

assertAll is valuable: without it, the first failed assertion stops the method and you never see the others.

Trap — argument order

assertEquals(expected, actual)expected first. Reversing them still compiles and passes/fails correctly, but the failure message ("expected X but was Y") is then backwards and misleading.

Asserting exceptions

JUnit 5 has no @Test(expected=...). Use assertThrows, which returns the exception so you can inspect it:

java
var ex = assertThrows(IllegalArgumentException.class, () -> svc.quote(-1, "EUR"));
assertTrue(ex.getMessage().contains("amount"));

Put only the throwing call in the lambda — not setup — so the assertion is precise about what must throw.

Assumptions — skip vs fail

An assumption that isn't met aborts (skips) the test; it doesn't fail it. Use it for environment preconditions:

java
assumeTrue(System.getenv("CI") == null, "skip locally-only test on CI");
// ... test body runs only when the assumption holds

Contrast: an assertion that fails marks the test red. Assumptions are for "this test doesn't apply here," assertions are for "the behavior is wrong."

SDET note

@DisplayName is how these labs read like sentences in the test report (M14·L01 — adds two positive numbers). Readable names double as living documentation; favor describing the behavior, not the method name.

Key Takeaways

  • Lifecycle: @BeforeEach/@AfterEach wrap every test; @BeforeAll/@AfterAll (static) run once. A new instance per test keeps tests isolated.
  • assertEquals(expected, actual)expected first; use a delta for doubles; assertAll to report every failure in a group.
  • Assert throwing with assertThrows, which returns the exception for further checks; keep only the throwing call in the lambda.
  • Assumptions abort/skip; assertions fail. Use assumptions for preconditions.
  • Name tests with @DisplayName describing behavior.

Lesson Quiz

Lesson Quiz · JUnit 5 Essentials0 / 5
  1. How often does @BeforeEach run?

    • AOnce per class
    • BBefore every @Test method
    • COnly before the first test
    • DAfter all tests
  2. Why must @BeforeAll be static (by default)?

    • AStyle preference
    • BIt runs before any test instance exists, so it can't use instance state
    • CStatic methods are faster
    • DIt isn't required to be static
  3. Correct argument order for assertEquals?

    • Aactual, expected
    • Bexpected, actual
    • COrder doesn't matter at all
    • Dmessage, value
  4. How do you assert that svc.run() throws IllegalStateException in JUnit 5?

    • A@Test(expected = IllegalStateException.class)
    • BassertThrows(IllegalStateException.class, () -> svc.run())
    • Ctry { svc.run(); } catch (Exception e) {}
    • DexpectException(...)
  5. assumeTrue(condition) is false. What happens to the test?

    • AIt fails
    • BIt is aborted (skipped), not failed
    • CIt passes as success
    • DCompile error

Next: Parameterized & Structured Tests. This module's lab is in labs/src/test/java/com/jse21/m14_testing/.