Learning Java - Testing & Quality Assurance
Series/Learn Java/Episode 17
Episode 17 of 24

Learning Java - Testing & Quality Assurance

This episode covers testing in Java: unit testing with JUnit 5, mocking with Mockito and the test-driven development approach, integration testing, contract testing, and test containers, plus static analysis and linting tooling with SpotBugs, Checkstyle, and PMD.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Code without tests is code that is assumed broken. Episode 17 covers testing and quality assurance in Java — from unit testing with JUnit 5, mocking with Mockito, to integration testing, contract testing, and static analysis. You will build a safety net that makes refactoring safe.

Testing is not a formality; it is a culture. This episode introduces the tools and patterns that professional teams use to maintain code quality continuously.

Unit Testing with JUnit 5

Adding JUnit 5

JUnit 5 is the standard Java testing framework. Add the dependency:

Add JUnit 5
mvn dependency:get -Dartifact=org.junit.jupiter:junit-jupiter:5.10.3

Writing Your First Unit Test

The test structure: a class in src/test/java, methods with the @Test annotation and assertions:

Unit test with JUnit 5
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
 
class KalkulatorTest {
 
    @Test
    void tambahMenghasilkanJumlah() {
        Kalkulator kalkulator = new Kalkulator();
        assertEquals(5, kalkulator.tambah(2, 3));
    }
 
    @Test
    void bagiDenganNolMelemparException() {
        Kalkulator kalkulator = new Kalkulator();
        assertThrows(ArithmeticException.class,
            () -> kalkulator.bagi(10, 0));
    }
}

assertEquals(5, kalkulator.tambah(2, 3)) verifies the expected result. Other assertions: assertTrue, assertNull, and assertThrows.

Running the Test

Run with Maven:

Run unit tests
mvn test

mvn test runs all tests in src/test/java and reports the results.

Mocking with Mockito and the TDD Approach

Why Mocking

Mocking simulates external dependencies (databases, APIs) so unit tests focus on logic alone. Mockito is the most popular mocking library:

Mocking with Mockito
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
 
class LayananTest {
 
    @Test
    void ambilDataMemakaiRepository() {
        Repositori repo = mock(Repositori.class);
        when(repo.cari(1L)).thenReturn("Data");
 
        Layanan layanan = new Layanan(repo);
        assertEquals("Data", layanan.ambil(1L));
        verify(repo).cari(1L);
    }
}

mock(Repositori.class) creates a mock object, when(...).thenReturn(...) defines its behavior, and verify ensures the method was called.

Test-Driven Development

TDD works in short cycles: write a failing test, create the minimal implementation to make it pass, then refactor. This red-green-refactor cycle forces you to think about the specification before implementation.

Integration Testing, Contract Testing, and Test Containers

Integration Testing

An integration test verifies collaboration between components: the application with a database, message broker, or external service. These tests use real environments, not mocks.

Test Containers

Testcontainers runs real dependencies (databases, brokers) inside Docker while the test runs:

Testcontainers with PostgreSQL
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.*;
 
@Testcontainers
class IntegrationTest {
 
    @Container
    static PostgreSQLContainer<?> postgres =
        new PostgreSQLContainer<>("postgres:16");
 
    @Test
    void koneksiKeDatabaseNyata() {
        String url = postgres.getJdbcUrl();
        assertNotNull(url);
    }
}

new PostgreSQLContainer<>("postgres:16") starts a real PostgreSQL in Docker for realistic tests.

Contract Testing

Contract testing verifies that two communicating services agree on the same API contract — preventing silent integration breakage. Popular tools: Spring Cloud Contract and Pact.

Static Analysis and Linting Tooling

SpotBugs, Checkstyle, and PMD

Static analysis inspects code without running it:

  • SpotBugs: detects real bugs through bytecode analysis.
  • Checkstyle: checks code style and conventions.
  • PMD: detects problematic code and code smells.

Add them as Maven plugins:

Maven plugins for static analysis
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-checkstyle-plugin</artifactId>
      <version>3.4.0</version>
    </plugin>
    <plugin>
      <groupId>com.github.spotbugs</groupId>
      <artifactId>spotbugs-maven-plugin</artifactId>
      <version>4.8.6</version>
    </plugin>
  </plugins>
</build>

Run them during the build:

Run static analysis
mvn verify

mvn verify runs tests and static analysis together, making it an automatic quality gate.

Closing

Episode 17 covers testing and QA: unit testing with JUnit 5, mocking with Mockito and the TDD approach, integration testing, contract testing, and test containers, plus static analysis with SpotBugs, Checkstyle, and PMD.

Key takeaways:

  • JUnit 5 with @Test and assertions is the basis of unit testing.
  • Mockito simulates external dependencies in tests.
  • TDD follows the red-green-refactor cycle.
  • Testcontainers runs real dependencies in Docker.
  • Contract testing prevents broken integration between services.
  • SpotBugs, Checkstyle, and PMD become an automatic quality gate.

In the next episode, episode 18, we will discuss modular programming and project architecture — the modern Java Platform Module System, modularizing applications with module-info.java and dependency encapsulation, clean architecture, package-by-feature, and layered architecture, plus the important builder, factory, strategy, and observer design patterns. Time to design clean architecture!