Chapter 13 of 16Foundation

Testing Basics with JUnit

Write framework-free unit tests with arrange-act-assert, meaningful assertions, and failure-path coverage.

25 minJava 21
01

Why it matters

Start from the concept, not the syntax

Meet a concrete situation where this idea earns its place, then state the boundary it guards.

A unit test constructs the object under test directly, controls its collaborators, and proves one observable behavior. Clean arrange-act-assert separation gives failure reports that explain exactly what broke.

By the end of this chapter

Testing private fields or asserting solely on code coverage leads to brittle tests that break during safe refactorings while missing edge-case bugs.

  • Arrange-act-assert
  • Assertions and failure paths
  • Test isolation
02

Mechanism

How Java expresses the concept

Walk the actual types, rules, and runtime behavior involved—the same behavior described in the Java Language Specification.

The mental model

Arrange the inputs and collaborators, act through one public operation, and assert the result or state change.

Test behavior rather than private implementation details so a safe refactor does not break the test.

Cover normal behavior, invalid input, and important boundaries. Line coverage alone is not confidence.

Keep unit tests isolated; use integration tests for databases, networks, and framework wiring.

03

Concept map

Inspect the building blocks as a whole

Use the map as a structural reference; it shows the pieces and their relationships, not an execution sequence.

Core Java

Testing behavior with JUnit

A unit test constructs the object under test directly, controls its collaborators, and proves one observable behavior.

  1. 01Arrange-act-assert
  2. 02JUnit 5 @Test
  3. 03Behavior assertions
  4. 04Failure paths
  5. 05Test isolation
04

Implementation

Encode the smallest runnable example

Write and run the code, then observe the behavior you just predicted with a real Java program.

Runnable snippet path

Predict, run, and change each example

2 focused examples
01

Assert success and failure

The test proves both valid behavior and the invariant that rejects invalid input.

PriceTest.java
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;

record Price(int cents) {
    Price {
        if (cents < 0) throw new IllegalArgumentException("negative price");
    }
}

class PriceTest {
    @Test
    void accepts_a_valid_price() {
        assertEquals(100, new Price(100).cents());
    }

    @Test
    void rejects_a_negative_price() {
        assertThrows(IllegalArgumentException.class, () -> new Price(-1));
    }
}

Remember

Assert the public contract, not implementation details.

Change one input, predict the result, then compile or test it before continuing.

02

Keep collaborators isolated

A fake keeps a unit test deterministic without testing a database at the same time.

TaskServiceTest.java
class TaskServiceTest {
    @Test
    void completes_a_task() {
        var repository = new InMemoryTaskRepository();
        var service = new TaskService(repository);

        service.complete(42);

        assertEquals("COMPLETED", repository.statusOf(42));
    }
}

Remember

Label tests that use a real repository as integration tests.

Change one input, predict the result, then compile or test it before continuing.

05

Watch out

Break it on purpose, then repair the right layer

Predict the symptom before running the failure case, and close with the completion checklist.

Deliberate failure

  1. Break

    Assert private fields with reflection instead of public contract behavior

  2. Observe

    Refactoring internal representation breaks tests even when public behavior is intact

  3. Repair

    Test observable behavior and public contracts so unit tests remain robust through refactors

Why it matters

Test observable behavior and public contracts so unit tests remain robust through refactors

Completion checklist

  • Structure tests with arrange, act, assert
  • Assert both successful results and expected exceptions
  • Keep unit tests fast, repeatable, and isolated
06

Go deeper

Official references

The primary sources behind this chapter. Every claim above stays consistent with them.