Characteristics of Good Unit Tests
This article is a glossary entry on characteristics of good unit tests – including exam questions and tags.
In a Nutshell
Good unit tests are correct, isolated, fast, meaningful, maintainable, and easy to execute. They provide quick, reliable feedback and enable safe refactoring.
Compact Technical Description
1. Correct
Test verifies what it should verify – no false assumptions, no random results.
2. Isolated
No dependencies on external systems (DB, network, file system). Instead, use test doubles (stubs, mocks, fakes).
3. Fast
Execution in the millisecond range, so hundreds of tests can run in seconds (CI feedback).
4. Meaningful
Clear test names, understandable Arrange/Act/Assert structure, precise assertions with meaningful error messages.
5. Maintainable
No duplicated code (DRY), shared helper libraries, refactoring-safe.
6. Easy to Execute
One command starts all tests, no manual setup required, deterministic results.
Exam-Relevant Key Points
- AAA (Arrange, Act, Assert) as structure
- Test doubles: Stub (predefined responses) vs. Mock (verify expectations)
- Naming:
methodName_condition_expectedResult - Explicitly test edge cases and error scenarios
- No “magic” numbers, use constants
- Meaningful assertions (e.g.,
assertEquals(expected, actual, message)) - Make tests independent (order doesn’t matter)
- Deterministic (no randomness, no time dependencies)
Core Components
- Test structure (AAA)
- Test doubles (Stub, Mock, Fake, Spy)
- Naming conventions
- Assertions (clear error messages)
- Test data (Factories, Builder)
- Isolation (no external dependencies)
- Speed (ms range)
- Independence (order doesn’t matter)
- Determinism (no randomness/time)
- Maintainability (DRY, refactoring-safe)
Practical Example (Discount Calculation)
// Naming: calculateDiscount_newCustomer_standardItem_expectsZeroPercent
@Test
public void calculateDiscount_newCustomer_standardItem_expectsZeroPercent() {
// Arrange
Customer customer = new Customer(CustomerType.NEW);
Item item = new Item(ItemType.STANDARD);
DiscountService service = new DiscountService();
// Act
int discount = service.calculateDiscount(customer, item);
// Assert
assertEquals(0, discount, "New customer should receive no discount");
}
Advantages and Disadvantages
Advantages
- Quick feedback (ms)
- High fault localization
- Safe refactoring
- Low maintenance costs with good structure
Disadvantages
- Initial effort for test doubles
- Risk of over-engineering with too many helper libraries
Typical Exam Questions (with Short Answer)
- What does AAA mean? Arrange (prepare), Act (execute), Assert (verify).
- Difference between Stub and Mock? Stub provides fixed responses; Mock verifies that interactions occurred.
- Why is isolation important? Prevents external systems from slowing down tests or making them unreliable.
- What is the naming convention for good tests?
methodName_condition_expectedResult.
Most Important Sources
- https://martinfowler.com/articles/practical-test-pyramid.html
- https://junit.org/junit5/docs/current/user-guide/
- https://testing.googleblog.com