Course
Learn Java with Tests
A test-driven development course for Java, inspired by Learn Go with Tests. Every chapter is a tiny, self-contained package: read the failing test, implement the minimum to make it pass, refactor.
Start here — clone the blank template
This course is a do, not a read. Clone the blank starter project — Gradle, JUnit 5 and AssertJ are already wired — and you build each chapter's package yourself:
git clone https://github.com/gdguesser/learn-java-with-tests-starter.git
cd learn-java-with-tests-starter
./gradlew test # toolchain ready, no tests yet./gradlew test is the loop that drives every chapter; on Windows use gradlew.bat test. Then open chapter 01 and create src/main/java/hello and src/test/java/hello yourself — your first failing run (a "cannot find symbol" compile error) will be your first RED.
The solutions repo contains all 18 chapters written and tested — compare against it once a chapter is green. Full setup walkthrough (JDK install, editors, troubleshooting): Getting started.
Write a failing test
Describe one piece of behaviour as a test before any production code exists.
Make it pass
Write the minimum code to satisfy the test — no more than that.
Refactor
Clean up the code while the green tests keep you safe. Repeat.
Chapters
- 01Hello, worldYour first JUnit test, assertEquals, and the RED-GREEN-REFACTOR loop.
- 02IntegersassertEquals on primitives and parameterized tests with @CsvSource.
- 03IterationLoops, StringBuilder, and refactoring to String.repeat.
- 04ListsList.of, enhanced for-loops, and your first streams refactor.
- 05Structs (records)Records as immutable data with behaviour, and float comparisons with a delta.
- 06InterfacesPolymorphism behind a Shape contract, tested through the interface.
- 07MapsWord counting with Map, merge, and a streams groupingBy refactor.
- 08Streamsfilter, map, and reduce — the functional way to process collections.
- 09OptionalsDesigning absence with Optional instead of null.
- 10ExceptionsassertThrows, assertAll, and exceptions as part of the contract.
- 11Dependency injectionConstructor injection, @FunctionalInterface, and the composition root.
- 12MockingHand-rolled fakes and spies — test the choreography, not the wait.
- 13ConcurrencyExecutorService, CompletableFuture, and virtual threads — submit everything, then join.
- 14Select & timeoutsCompletableFuture.anyOf to race tasks, plus timeouts that fail loudly.
- 15SynchronizationData races, synchronized, LongAdder, and ConcurrentHashMap — correctness by construction.
- 16ReflectionClass, Field, and Method — the machinery behind frameworks you almost never need yourself.
- 17Real project: an HTTP serverThe JDK's HttpServer and HttpClient — I/O at the edge, decisions in a testable core.
- 18AssertJFluent assertions that read like sentences and fail with useful diffs.