agentsclimarketplace

Springboot tdd

Skill Lukk17/agent-standards/.agents/skills/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

Install
npx -y skills add Lukk17/agent-standards --skill springboot-tdd

Assembled 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

  1. Write a failing test first (they should fail)
  2. Implement minimal code to pass
  3. Refactor with tests green
  4. 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 @ParameterizedTest for 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 @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.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.