Introduction to Nullables
A Learning Hour on replacing mocks with Nullables, using a Library kata in Java.
Welcome to the Introduction to Nullables Learning Hour. We take a small Library that lends books and prints overdue reminders, and replace its mocks with Nullables — real instances of your own code wired to run without talking to the outside world.
Table of Contents
- Learning Goals 🎯
- Connect (⏱️ 5 min)
- Concept (⏱️ 10 min)
- Concrete Practice — from Mockito to Nullables (⏱️ 35 min)
- Conclusions (⏱️ 5 min)
- Attribution
Learning Goals 🎯
- Understand what a Nullable is and how it differs from a mock.
- Use a ready-made Nullable to write a unit test.
- Give a class its own
createNull()factory and use it to control and observe its behaviour.
Connect (⏱️ 5 min)
Ask the group:
What are examples of dependencies that have caused you problems in tests — making them slow, flaky, or hard to maintain?
Have everyone write their answers on sticky notes (databases, clocks, file systems, third-party APIs, message queues…) and share a few out loud. This surfaces the pain that Nullables address and connects the topic to the team’s own codebase.
Concept (⏱️ 10 min)
How easy is it to test these?
Draw a 2×2 matrix with ownership on the horizontal axis (not yours → yours) and communication on the vertical axis (self-contained → talks to the outside):
| Not yours | Yours | |
|---|---|---|
| Talks to the outside | 😡 | 😬 |
| Self-contained | 🙂 | 😎 |
Place the dependencies from the Connect activity on the matrix. The insight to land: talking to the outside is what makes testing painful, whether the code is yours or not.
Nullables: production code with an OFF switch
The technique comes from James Shore (Testing Without Mocks). His one-line definition:
Nullables are production code with an “OFF” switch.
- Real production code — not a hand-written imitation of it.
- Code that doesn’t talk to the outside world — the external communication is switched off; everything inside your system still runs for real.
Two ways to build the same object
Show the two factory methods side by side:
class ApplicationController {
// instantiates your real code, talks to the outside world
static ApplicationController create() { /* ... */ }
// same real code, external communication switched off
static ApplicationController createNull() { /* ... */ }
}
create()— normal mode: instantiates your real code that wires up a client that talks to another system over the network (Controller → Client → network → another system).createNull()— nulled mode: instantiates the same real code, but communication with the outside is turned OFF (the network hop is blocked).
Concrete Practice — from Mockito to Nullables (⏱️ 35 min)
All four exercises live in the java/ implementation. Follow the comments in each test and make it pass.
Work in pairs or ensemble on the java/ implementation of the Library kata. Setup instructions are in java/README.md.
Exercise 1 — Test with a mock (⏱️ 5 min timebox)
- Run the tests — one should be failing.
- Open
java/README.md. - Make
_1_MockedLoanTest(in thelegacyfolder) pass using Mockito.
Timebox to 5 minutes and stop even if unfinished. The point is to feel the friction, not to finish.
Then debrief with a sharper question than “how did it feel?”:
How much of those 5 minutes did you actually spend thinking about the test case — the scenario you were trying to verify?
Rate it (😡 🙂 😬 😎) and jot a note.
Exercise 2 — Test with a Nullable (⏱️ ~10 min)
The Nullables have already been created for you (refactored folder) — frame it as “a colleague refactored the system to Nullables while you were away.” Use them instead of Mockito to pass:
_2_1_NullLoanTest— start with the simplest scenario: an empty library sends no reminders (nobody borrowed a book, so nobody gets reminded)._2_2_NullLoanTest— a book borrowed but not yet due isn’t overdue._2_3_NullLoanTest— from a mix of loans, only the past-due books come back.
Debrief with the same question — how much of the 5 minutes went into the test case itself? This time expect “it was easy”, “nice to read”, “easy to concentrate on the business logic”.
Demo — creating a simple Nullable (⏱️ ~5 min)
Show how a createNull() factory is built — swap the real infrastructure for an embedded, in-memory stand-in while keeping the real logic. This sets up participants to write their own.
Exercises 3 & 4 — Create a simple Nullable (⏱️ ~15 min)
- Open
java/README.mdand follow Exercise 3:Libraryneeds a database, a printer, and the wall clock. Its dependencies are already Nullables — giveLibraryits owncreateNull(). File:_3_1_LibraryTest. - Then Exercise 4: control and observe your world — tell the
Librarywhat books are borrowed and check which reminders it prints. Files:_4_1_LibraryTest,_4_2_LibraryTest.
How did it feel? One last rating and note.
Conclusions (⏱️ 5 min)
Close with two questions and collect answers on sticky notes:
If you had to explain Nullables to a colleague, what would you say?
What was the most interesting thing you learned today?
Close by collecting feedback on the session itself — what worked, what to change for next time.
Attribution
This Learning Hour is based on the Introduction to Nullables Miro board and kata template by Lada Kesseler, shared under a Creative Commons Attribution-ShareAlike license.