Unit 15 · lesson

JUnit Turns Expected Behavior Into Executable Evidence

Full JDK lane: this lesson's authentic JUnit execution requires a normal Java project environment with JUnit 6 available through your approved IDE/build tool. The browser core can still complete the same test-design exercise with the deterministic harness from Lesson 1.

JUnit gives tests a standard structure and runner.

A basic Jupiter test looks like:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

class ScoreClassifierTest {

    @Test
    void scoreAtNinetyIsHigh() {
        assertEquals("HIGH", ScoreClassifier.classify(90));
    }
}

Read the test as a claim

@Test
  marks executable test behavior

scoreAtNinetyIsHigh
  names the scenario/expectation

assertEquals(expected, actual)
  compares the requirement's expected value with the observed method result

The annotation is not the interesting part. The quality of the test comes from the case and assertion.

Arrange, Act, Assert

A useful test organization is:

Arrange -> create inputs/state
Act     -> call the behavior
Assert  -> compare observable result to expectation

Example:

@Test
void completedCannotExceedTotal() {
    int completed = 11;
    int total = 10;

    boolean actual = AttemptRules.isValid(completed, total);

    assertEquals(false, actual);
}

For simple tests, explicit comments labeled Arrange/Act/Assert are optional. The important thing is that the structure remains understandable.

Test exceptions when failure is the contract

If invalid input should throw, use an exception assertion in the full-JDK lane:

import static org.junit.jupiter.api.Assertions.assertThrows;

@Test
void zeroTotalIsRejected() {
    assertThrows(IllegalArgumentException.class,
        () -> AttemptRules.percent(0, 0));
}

Now failure is expected behavior, not a surprise that makes the test suite useless.

One test should communicate one behavioral idea

A test method with twenty unrelated assertions can become hard to diagnose. When it fails, the name may no longer identify which behavior broke.

Group assertions when they describe one coherent scenario. Split them when the behaviors have independent reasons to fail.

Full-JDK evidence

Create a tiny test class for one pure calculation/validation class from earlier units.

Include:

  • normal case;
  • lower boundary;
  • upper or threshold boundary;
  • invalid case;
  • one regression case tied to a previous defect.

Record the runner output showing test names/results plus the source of the test class.

Browser-core equivalent

Use the same cases in the check(name, expected, actual) harness.

Label the artifact correctly:

JUnit execution: not performed in this environment
Test-design evidence: performed with deterministic browser harness

Do not fabricate dependency-managed JUnit evidence. The conceptual target is still test design, and the environment boundary remains visible.