Skip to content

Instantly share code, notes, and snippets.

@sormuras
Last active September 18, 2025 13:33
Show Gist options
  • Select an option

  • Save sormuras/000df9d23f2ecd7334121eb740dca2f6 to your computer and use it in GitHub Desktop.

Select an option

Save sormuras/000df9d23f2ecd7334121eb740dca2f6 to your computer and use it in GitHub Desktop.
JUnit's On-Ramp

JUnit's on-ramp

This article is a guide for beginners in the early stages of their Java learning journey. It explores an introduction to JUnit using a minimal set of tools. Exploring a non-JDK API shouldn't be much harder to write and run then the typical "Hello World" Java program.

Prolog

In Paving the on-ramp Brian Goetz explains the goal and role of Java's on-ramp in great detail. You may want to read that design document first.

Updated to work with the source file launcher of Java 25, the minimal "Hello World" Java program presented in the "Paving the on-ramp" design document is:

void main() {
  IO.println("Hello World");
}

Users run it on the consoler with calling:

java HelloWorld.java

and immediately see a result on the console:

Hello World

They edit the HelloWorld.java file as they apply new learnings, run the program again, and see the result being impacted by their code changes. Rinse and repeat. Users naturally jump into the "modify-execute-verify loop" without noticing that they perform manual software testing!

From Manual to Automated Testing

A key goal in software development is the automation of manual work. Where applicable, automated work is usally computed orders of magnitude faster and also more reliable compared to manual work. Automated software testing aims to codify the "verify" part of the "modify-execute-verify loop" mentioned in the previous section.

JUnit is the programmer-friendly testing framework for Java and the JVM that helps users write and run automated software tests.

The User-Guide of JUnit states:

The goal of this document is to provide comprehensive reference documentation for programmers writing tests, extension authors, and engine authors as well as build tool and IDE vendors.

That's a broad goal for a large and technical advanced audience. It even splits the sections regarding writing tests and running tests far apart. It also focuses on the presence of build tools and IDEs; which might not be in hands of users on the Java on-ramp, yet. The provided junit console launcher is the closest users can get to run compile test classes without external tool support. But compiling Java classes with javac is no longer needed on Java's on-ramp. Beginners learn how to write statements in a program - so let's build on that.

HelloTests.java

So, what's the minimal JUnit test a beginner can write and run in a "modify-execute-verify loop" using Java's source launcher?

import module org.junit.onramp;

void main() {
  JUnit.run(this);
}

@Test
void addition() { 
  Assertions.assertEquals(2, 1 + 1, "Addition error detected!");
}

With all required JAR files stored in a local lib directory TODO, users need to make JUnit available on the module path and use the java launcher as before with their HelloWorld program:

java --module-path lib --add-modules org.junit.onramp HelloTests.java

Done!

TODO Paste results from the console

Line-by-line explanation:

  • import module org.junit.onramp; - for writing and running tests
  • JUnit.run(this); - let JUnit discover and execute @Test-annotated methods in this Java class

Users may ask: "But why give control to a framework instead of calling test methods directly?"

void main() {
  addition();
}

void addition() {
  if (2 != 1 + 1) throw new AssertionError("Addition error detected!");
}

It's easy to remember to attach an @Test annotation to a method. Adding an explicit call of new test methods is error-prone, cumbersome, and hard-wired.

Additionally, JUnit offers:

  • More annotations for specialized forms of tests - repeated, parameterized, generated, ...
  • Assertions and Assumptions - hard and soft condition checkers
  • Dependency Injection - get information about the test method and its context at runtime
  • Lifecycle support - set test fixtures up and tear them down gracefully
  • Selection and filter support - run a subset of interesting tests
  • Summary and report generation - see which tests were run, what went wrong and where
  • Want to know all? Consult the JUnit User Guide and the JUnit API documentation.

TODO Add more questions and answers

Before presenting alternatives, Brian summarizes:

This seems to meet the requirements of our on-ramp; we’ve eliminated most of the day-1 ceremony elements without introducing new concepts that need to be unlearned. The remaining concepts — a method is a container for statements, and a program is a Java source file with a main method — are easily understood in relation to their fully specified counterparts.

Same goes for the minimal JUnit test class:

  • a test method is still a container for statements, with purpose of setup and result assertion
  • a test program is Java source file in with main method; in which the control is given to JUnit
  • JUnit discovers methods with an @Test annotation and gives control back

Right, users just also entered the realm of Inversion of Control.

Coda

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment