Theory: Harness, Stubs & Drivers
In theory a unit is tested completely alone. In real life a unit almost never lives alone: it calls other modules, and other modules call it. Because we test while we develop, many of those neighbours have not been written yet. This lesson introduces the fake helpers that fill the gaps, and the name for everything they form together.
Why We Need Fakes
Think of a theatre rehearsal. The lead actor is ready, but the actor who plays the messenger is ill, and the stage manager has not yet built the door the lead must walk through. The rehearsal goes ahead anyway: someone stands in and reads the messenger’s two lines from a script, and a strip of tape on the floor marks where the door will be. The stand-ins do only the minimum, but the lead can rehearse properly.
Testing a unit works the same way. The real modules around it may not exist yet, so we build simple fake versions that do just enough for the test to run.
The Harness: Driver Above, Stubs Below
Diagram loads as you scroll.
- Stub (the professor also called it a mock object): a fake version of a module that the unit calls. It has only the minimum functionality needed for the test. Example: a fake database that always returns the same customer, so the unit can be tested without a real database.
- Driver: a small program that calls the unit with test inputs and collects the results (you met it in 7.4).
- Harness (or scaffolding): all the set-up around the unit, drivers and stubs together, that lets us control the inputs and observe the outputs. Like scaffolding on a building, it is essential while the work is going on and is not part of the finished product.
JUnit gives you a ready-made harness: the test class plays the role of the driver, and the framework runs it, collects the results and reports failures and errors (see 8.5).
Stub = fake module BELOW (it is called). Driver = fake caller ABOVE (it calls).
In the rehearsal picture, the person reading the messenger’s lines is a stub: the lead actor speaks to them and they answer. A director calling “action!” and watching what the lead does is a driver.
Common mistake: mixing up the two by what they are (both are fake code) instead of by the direction of the call. Ask: “Is the missing module the one that calls my unit, or one that my unit calls?” Missing caller → driver. Missing callee → stub.
A finer vocabulary of fakes (dummy, stub, spy, mock, fake) appears in 8.5. In class the professor used stub and mock object loosely for the same idea: a minimal stand-in for a module the unit calls.
Worked Examples
Exercise 1
Why do we need stubs and drivers at all? Isn’t a unit supposed to be tested alone?
Show solution ↓Hide solution ↑
Solution
In theory a unit is tested alone, but in real life a unit calls other modules or is called by them. Because we test while we develop, those modules may not be written yet. Without them the unit cannot even run: nothing would call it, and the calls it makes would have nowhere to go.
Stubs and drivers are fake stand-ins for the missing neighbours, so the unit can be run, controlled and observed before the rest of the system exists.
Exercise 2
Define stub, driver and harness, each in one sentence.
Show solution ↓Hide solution ↑
Solution
- Stub: a fake module with minimum functionality that is called by the unit under test, replacing a lower module that is not available.
- Driver: a small program that calls the unit under test with test inputs and collects its results.
- Harness (scaffolding): all the set-up around the unit, drivers and stubs together, that lets us control its inputs and observe its outputs.
Exercise 3
A method calculateInvoice() reads customer data through a CustomerRepository that will talk to a database, but the database layer has not been written. What do you build to test calculateInvoice(), and what should it do?
Show solution ↓Hide solution ↑
Solution
CustomerRepository is a module that the method under test calls, so the missing piece sits below the unit: build a stub (a mock object).
The stub should have only the minimum functionality the test needs: for example, a fake repository that always returns one fixed customer with known data. Because the test then knows exactly what data the method receives, it can work out the expected invoice in advance and compare it with the actual result.
Exercise 4
A low-level module TaxRules is finished, but the Checkout module that will call it has not been written. What do you need to test TaxRules now?
Show solution ↓Hide solution ↑
Solution
The missing module is the caller, which sits above TaxRules, so you need a driver: a small test program (for example a JUnit test class) that calls TaxRules with chosen inputs and checks the results against the expected outputs.
Exercise 5
In what sense is JUnit a “ready-made harness”?
Show solution ↓Hide solution ↑
Solution
A harness is everything around the unit that lets us control its inputs and observe its outputs. JUnit provides most of that for free: our test class acts as the driver (its test methods call the unit with chosen inputs), fixtures put every test into a known starting state, assertions compare actual with expected results, and the framework runs all the tests and reports each failure and error. We only have to write the test methods and, where the unit calls missing modules, any stubs.