home / skills / affaan-m / everything-claude-code / springboot-tdd

springboot-tdd skill

/skills/springboot-tdd

This skill guides Spring Boot TDD workflows with JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo to ensure fast, reliable tests.

npx playbooks add skill affaan-m/everything-claude-code --skill springboot-tdd

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.7 KB
---
name: springboot-tdd
description: Test-driven development for Spring Boot using JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo. Use when adding features, fixing bugs, or refactoring.
---

# Spring Boot TDD Workflow

TDD guidance for Spring Boot services with 80%+ coverage (unit + integration).

## When to Use

- New features or endpoints
- Bug fixes or refactors
- Adding data access logic or security rules

## Workflow

1) Write tests first (they should fail)
2) Implement minimal code to pass
3) Refactor with tests green
4) Enforce coverage (JaCoCo)

## Unit Tests (JUnit 5 + Mockito)

```java
@ExtendWith(MockitoExtension.class)
class MarketServiceTest {
  @Mock MarketRepository repo;
  @InjectMocks MarketService service;

  @Test
  void createsMarket() {
    CreateMarketRequest req = new CreateMarketRequest("name", "desc", Instant.now(), List.of("cat"));
    when(repo.save(any())).thenAnswer(inv -> inv.getArgument(0));

    Market result = service.create(req);

    assertThat(result.name()).isEqualTo("name");
    verify(repo).save(any());
  }
}
```

Patterns:
- Arrange-Act-Assert
- Avoid partial mocks; prefer explicit stubbing
- Use `@ParameterizedTest` for variants

## Web Layer Tests (MockMvc)

```java
@WebMvcTest(MarketController.class)
class MarketControllerTest {
  @Autowired MockMvc mockMvc;
  @MockBean MarketService marketService;

  @Test
  void returnsMarkets() throws Exception {
    when(marketService.list(any())).thenReturn(Page.empty());

    mockMvc.perform(get("/api/markets"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$.content").isArray());
  }
}
```

## Integration Tests (SpringBootTest)

```java
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
class MarketIntegrationTest {
  @Autowired MockMvc mockMvc;

  @Test
  void createsMarket() throws Exception {
    mockMvc.perform(post("/api/markets")
        .contentType(MediaType.APPLICATION_JSON)
        .content("""
          {"name":"Test","description":"Desc","endDate":"2030-01-01T00:00:00Z","categories":["general"]}
        """))
      .andExpect(status().isCreated());
  }
}
```

## Persistence Tests (DataJpaTest)

```java
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import(TestContainersConfig.class)
class MarketRepositoryTest {
  @Autowired MarketRepository repo;

  @Test
  void savesAndFinds() {
    MarketEntity entity = new MarketEntity();
    entity.setName("Test");
    repo.save(entity);

    Optional<MarketEntity> found = repo.findByName("Test");
    assertThat(found).isPresent();
  }
}
```

## Testcontainers

- Use reusable containers for Postgres/Redis to mirror production
- Wire via `@DynamicPropertySource` to inject JDBC URLs into Spring context

## Coverage (JaCoCo)

Maven snippet:
```xml
<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.8.14</version>
  <executions>
    <execution>
      <goals><goal>prepare-agent</goal></goals>
    </execution>
    <execution>
      <id>report</id>
      <phase>verify</phase>
      <goals><goal>report</goal></goals>
    </execution>
  </executions>
</plugin>
```

## Assertions

- Prefer AssertJ (`assertThat`) for readability
- For JSON responses, use `jsonPath`
- For exceptions: `assertThatThrownBy(...)`

## Test Data Builders

```java
class MarketBuilder {
  private String name = "Test";
  MarketBuilder withName(String name) { this.name = name; return this; }
  Market build() { return new Market(null, name, MarketStatus.ACTIVE); }
}
```

## CI Commands

- Maven: `mvn -T 4 test` or `mvn verify`
- Gradle: `./gradlew test jacocoTestReport`

**Remember**: Keep tests fast, isolated, and deterministic. Test behavior, not implementation details.

Overview

This skill codifies a practical TDD workflow for Spring Boot services using JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo. It guides you to write failing tests first, implement minimal code to pass, and enforce coverage and reliability. The goal is repeatable, fast, and maintainable test suites targeting 80%+ coverage for unit and integration tests.

How this skill works

The skill lays out patterns and example tests for unit, web layer, integration, and persistence testing. It recommends tooling and configurations: Mockito for unit doubles, MockMvc for controller tests, @SpringBootTest and @DataJpaTest for integration and repository scopes, Testcontainers for realistic databases, and JaCoCo to measure coverage. It also prescribes assertions, test data builders, and CI commands to run tests and reports.

When to use it

  • Adding a new endpoint or feature to a Spring Boot service
  • Fixing bugs or refactoring existing code safely
  • Introducing data access or security logic
  • Ensuring reliable behavior before merging to main
  • Setting up CI test and coverage verification

Best practices

  • Write tests first and let them fail, then implement minimal code to pass
  • Keep tests fast, isolated, and deterministic; avoid flaky external dependencies by using Testcontainers or mocks
  • Follow Arrange-Act-Assert and prefer explicit stubbing over partial mocks
  • Use AssertJ for readable assertions and jsonPath for JSON responses
  • Use parameterized tests for input variants and test data builders to simplify setup
  • Enforce coverage with JaCoCo and run tests in CI (mvn verify or ./gradlew test jacocoTestReport)

Example use cases

  • Unit-test a service method with Mockito and @ExtendWith(MockitoExtension.class)
  • Test a REST controller with @WebMvcTest and MockMvc to validate status and JSON shape
  • Run end-to-end behavior checks with @SpringBootTest and AutoConfigureMockMvc in an isolated test profile
  • Validate JPA repositories with @DataJpaTest backed by Testcontainers Postgres
  • Add JaCoCo to build to fail the pipeline when coverage drops below the team threshold

FAQ

How do I integrate Testcontainers with Spring properties?

Use @DynamicPropertySource to inject container JDBC/Redis URLs into the Spring context so beans pick up the runtime container endpoints.

What scope should I prefer for fast feedback?

Favor unit tests (JUnit + Mockito) for most logic. Use Web layer tests (@WebMvcTest) for controller behavior and run integration tests selectively to cover end-to-end flows and persistence.