PluginBench
Skill
Pass
Audit score 90

springboot-tdd

affaan-m/ecc

Test-driven development for Spring Boot with JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo coverage.

What is springboot-tdd?

Guidance for writing unit, integration, and persistence tests in Spring Boot services using TDD principles. Use when adding features, fixing bugs, or refactoring to maintain 80%+ code coverage and ensure reliable behavior.

  • Write unit tests with JUnit 5 and Mockito using Arrange-Act-Assert patterns
  • Test web layers with MockMvc to verify controller behavior and JSON responses
  • Run integration tests with @SpringBootTest to validate end-to-end flows
  • Test data persistence with @DataJpaTest and Testcontainers for production-like databases
  • Measure and enforce code coverage with JaCoCo reporting
  • Use test data builders and parameterized tests for maintainable test suites

How to install springboot-tdd

npx skills add null --skill springboot-tdd
Prerequisites
  • JUnit 5 and Mockito dependencies in Maven or Gradle
  • Spring Boot Test and MockMvc on the classpath
  • Testcontainers library for database testing (optional but recommended)
  • JaCoCo Maven or Gradle plugin configured for coverage reporting
Claude Code
Cursor
Windsurf
Cline

How to use springboot-tdd

  1. 1.Write failing tests first using @Test and Arrange-Act-Assert structure
  2. 2.Mock dependencies with @Mock and @InjectMocks or @MockBean for Spring beans
  3. 3.Use MockMvc to test controller endpoints with perform(), andExpect() chains
  4. 4.Configure Testcontainers with @DynamicPropertySource to inject database URLs into Spring context
  5. 5.Run mvn verify or ./gradlew test jacocoTestReport to execute tests and generate coverage reports
  6. 6.Review JaCoCo HTML reports to identify untested code paths and improve coverage

Use cases

Good for
  • Adding new REST endpoints or service methods with full test coverage
  • Fixing bugs by writing failing tests first, then implementing the fix
  • Refactoring existing code while keeping tests green to prevent regressions
  • Testing database queries and JPA repositories against real Postgres/Redis containers
  • Validating security rules and authorization logic in controllers and services
Who it's for
  • Spring Boot developers practicing test-driven development
  • Teams enforcing minimum code coverage thresholds (80%+)
  • Backend engineers building microservices with reliable test suites
  • QA engineers automating integration and persistence testing

springboot-tdd FAQ

Should I use partial mocks or explicit stubbing?

Prefer explicit stubbing with when(...).thenReturn(...) over partial mocks; it makes test intent clearer and reduces coupling to implementation details.

How do I test database queries without a real database?

Use @DataJpaTest with Testcontainers to spin up a real Postgres or Redis container that mirrors production, injecting the JDBC URL via @DynamicPropertySource.

What's the difference between @WebMvcTest and @SpringBootTest?

@WebMvcTest loads only the web layer and mocks services; @SpringBootTest loads the full context and is better for integration tests that need real beans.

How do I keep tests fast and deterministic?

Keep unit tests isolated with mocks, use parameterized tests for variants, avoid sleep() or time-dependent logic, and run integration tests separately in CI.

What assertion library should I use?

Use AssertJ (assertThat) for readable fluent assertions; combine with jsonPath for JSON response validation and assertThatThrownBy for exception testing.

Full instructions (SKILL.md)

Source of truth, from affaan-m/ecc.


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. metadata: origin: ECC

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)

@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)

@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)

@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)

@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:

<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

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.