Springboot tdd
Test-driven development for Spring Boot using JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo. Use when adding features, fixing bugs, or refactoring.From its SKILL.md
npx -y skills add Lukk17/agent-standards --skill springboot-tddAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.3 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it
Spring Boot TDD Workflow
TDD guidance for Spring Boot services with around 90% coverage of real logic (unit + integration), and 100% where it genuinely adds value.
When to Use
- New features or endpoints
- Bug fixes or refactors
- Adding data access logic or security rules
Workflow
- Write a failing test first (they should fail)
- Implement minimal code to pass
- Refactor with tests green
- Enforce coverage (JaCoCo)
Every test must satisfy FIRST: Fast (runs in milliseconds), Isolated (no dependence on other tests or shared state), Repeatable (same result on every run and every machine), Self-validating (a single pass/fail with no manual inspection), and Timely (written alongside or before the code, not bolted on afterwards). FIRST is the governing principle for this workflow.
Unit Tests (JUnit 5 + Mockito)
@ExtendWith(MockitoExtension.class)
class MarketServiceTest {
@Mock MarketRepository repo;
@InjectMocks MarketService service;
@Test
void createsMarket() {
// Given
CreateMarketRequest req = new CreateMarketRequest("name", "desc", Instant.now(), List.of("cat"));
when(repo.save(any())).thenAnswer(inv -> inv.getArgument(0));
// When
Market result = service.create(req);
// Then
assertThat(result.name()).isEqualTo("name");
verify(repo).save(any());
}
@Test
void create_whenRepositoryFails_propagatesException() {
// Given
CreateMarketRequest req = new CreateMarketRequest("name", "desc", Instant.now(), List.of("cat"));
when(repo.save(any())).thenThrow(new DataAccessResourceFailureException("db down"));
// When / Then
assertThatThrownBy(() -> service.create(req))
.isInstanceOf(DataAccessResourceFailureException.class);
verify(repo).save(any());
}
@Test
void create_withEmptyCategories_stillCreatesMarket() {
// Given
CreateMarketRequest req = new CreateMarketRequest("name", "desc", Instant.now(), List.of());
when(repo.save(any())).thenAnswer(inv -> inv.getArgument(0));
// When
Market result = service.create(req);
// Then
assertThat(result.categories()).isEmpty();
}
}
Patterns:
- Given / When / Then section comments in every test body
- Cover the happy path plus error and edge cases (failures, empty inputs, boundaries)
- Avoid partial mocks; prefer explicit stubbing
- Use
@ParameterizedTestfor variants
Web Layer Tests (MockMvc)
@WebMvcTest(MarketController.class)
class MarketControllerTest {
@Autowired MockMvc mockMvc;
@MockitoBean MarketService marketService;
@Test
void returnsMarkets() throws Exception {
// Given
when(marketService.list(any())).thenReturn(Page.empty());
// When / Then
mockMvc.perform(get("/api/markets"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content").isArray());
}
}
Use @MockitoBean for mocking Spring beans on Boot 3.4 and newer; the older @MockBean is deprecated. The
sibling springboot-verification skill must use the same annotation, so keep the two in sync.
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
@DynamicPropertySourceto 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 testormvn verify - Gradle:
./gradlew test jacocoTestReport
Remember: Keep tests fast, isolated, and deterministic. Test behavior, not implementation details.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 3 of the 12 instructions most tdd skills give in ~1.3k tokens
Counted across 439 of the 443 authors here whose files we hold, read 2026-08-07
- Write minimal code to pass the testhere, and in 304 of 439, across 222 files
- Write a failing test firsthere, and in 174 of 439, across 111 files
- Refactor code only after tests passin 172 of 439, across 102 files
- Watch the test fail before writing codein 145 of 439, across 97 files
- Test one behavior per testin 108 of 439, across 46 files
- Refactor code while keeping tests greenhere, and in 100 of 439, across 88 files
- Delete code written before testsin 99 of 439, across 55 files
- Run tests after each refactor stepin 88 of 439, across 57 files
- Confirm the test fails for the right reasonin 66 of 439, across 62 files
- Use real code instead of mocks unless unavoidablein 60 of 439, across 17 files
- Reproduce bugs with a test before fixingin 53 of 439, across 36 files
- Write tests before implementationin 51 of 439, across 43 files
Said here and by no other author read
- use mockitobean for mocking beans
- use reusable testcontainers for databases
- enforce coverage with jacoco
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.