28 · 32 · 5 min
Testing behaviour, not structure
Object oriented code hides state behind behaviour, which is good design and makes a specific testing mistake tempting: reaching for the state anyway.
Test through the public interface
@Test
void withdrawal_beyond_balance_is_rejected() {
var account = new Account("A-1", 1000);
assertThrows(InsufficientFunds.class, () -> account.withdraw(1500));
assertEquals(1000, account.balance());
}This asserts a rule the class promises. It survives any rewrite of the internals, which is the property that makes a test worth keeping.
A test that reads private fields by reflection, or that exists only because a method was made public to allow testing, is testing structure. It fails when you refactor and passes when you break behaviour, which is the change detector problem from the software engineering course.
Arrange, act, assert
@Test
void discount_applies_above_the_threshold() {
var order = new Order(); // arrange
order.add(new Item("book", 600));
var total = order.totalWith(new BulkDiscount(500, 10)); // act
assertEquals(540, total); // assert
}One act per test. Two actions in one test means a failure does not say which one broke.
The name is the specification
discount_applies_above_the_threshold tells you what broke from the report alone. testOrder3 requires opening the file. Listing the test names of a class should read as its contract.
What makes a class hard to test
Difficulty testing is a design signal, not a testing problem:
| Symptom | Design cause |
|---|---|
| Needs a database to construct | dependencies built internally |
| Needs eight arguments to set up | the class does too much |
| Assertions on time are flaky | the clock is not injected |
| Must call three methods in order first | leaking internal sequencing |
| Only testable through the UI | logic living in the wrong layer |
Each row is fixed by changing the class, not by writing a cleverer test.
What not to test
Getters and setters that do nothing. Framework behaviour. Third party libraries. Private methods directly, which are exercised through the public ones that use them.
Testing trivia inflates the count and the coverage number and catches nothing, and the tests still have to be maintained.