Module Review: Testing & Quality
[!NOTE] This review covers the essential techniques and tools for maintaining high code quality in Java, including JUnit 5 platform, Testcontainers, and architectural testing with ArchUnit.
Key Takeaways
- JUnit 5 Platform: It’s not just a framework; it’s a platform (API vs Engine) that allows multiple testing tools to coexist.
- Parameterized Tests: Stop copying and pasting tests. Use
@ParameterizedTestto run the same logic against multiple inputs. - Testcontainers: Don’t mock the database. Use ephemeral Docker containers to test against the real thing (PostgreSQL, Kafka) with perfect isolation.
- ArchUnit: Architecture diagrams are useless if the code doesn’t follow them. Use ArchUnit to enforce layers, cycles, and naming conventions as code.
- Hardware Reality: Parallel test execution increases throughput but introduces context switching overhead and shared state risks.
Flashcards
@BeforeAll
What is the scope of this annotation?
Class Level
It runs once before any test method in the class. Must be static (unless using @TestInstance(PER_CLASS)).
Dynamic Port Mapping
Why does Testcontainers bind to random ports?
To Avoid Conflicts
It allows multiple tests (or CI jobs) to run in parallel on the same machine without "Port 5432 already in use" errors.
ArchUnit Speed
Why is ArchUnit faster than reflection?
Bytecode Analysis
It inspects the compiled bytecode directly without loading classes into the JVM ClassLoader.
Extension Model
What replaces Rules and Runners in JUnit 5?
Extensions
A unified model using @ExtendWith to register callbacks (e.g., BeforeEachCallback, ParameterResolver).
Cheat Sheet
| Concept | Annotation / Class | Usage |
|---|---|---|
| Lifecycle | @BeforeEach, @AfterAll |
Setup/Teardown logic. |
| Display Name | @DisplayName("Name") |
Custom test name in reports. |
| Parameterized | @ParameterizedTest |
Mark method as parameterized. |
| Data Source | @ValueSource, @CsvSource |
Provide input arguments. |
| Testcontainers | GenericContainer<?> |
Base class for Docker containers. |
| Wait Strategy | Wait.forLogMessage() |
Ensure service is ready before testing. |
| Arch Rule | noClasses().should()... |
Define an architectural constraint. |
| Layer Check | layeredArchitecture() |
Define valid layer dependencies. |
Quick Revision
- JUnit 5 Architecture: Separates API (for writing tests) from the Engine (for discovering and executing tests).
- Parameterized Tests: Allows running the same test logic multiple times with different inputs using
@ParameterizedTest. - Testcontainers: Uses real Docker containers for dependencies (e.g., databases) in integration tests, ensuring environment parity.
- Test Isolation vs. Speed: Spinning up a container per test offers perfect isolation but is slow; using a singleton container for the entire suite is faster.
- ArchUnit: A static analysis tool to enforce architectural rules (e.g., layer boundaries, naming conventions) via standard unit tests.
- ArchUnit Performance: Fast because it analyzes bytecode directly without loading classes into the JVM ClassLoader.
Next Steps
Now that you’ve mastered testing, verify your understanding with the Java Glossary.