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

  1. JUnit 5 Platform: It’s not just a framework; it’s a platform (API vs Engine) that allows multiple testing tools to coexist.
  2. Parameterized Tests: Stop copying and pasting tests. Use @ParameterizedTest to run the same logic against multiple inputs.
  3. Testcontainers: Don’t mock the database. Use ephemeral Docker containers to test against the real thing (PostgreSQL, Kafka) with perfect isolation.
  4. ArchUnit: Architecture diagrams are useless if the code doesn’t follow them. Use ArchUnit to enforce layers, cycles, and naming conventions as code.
  5. 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.