Springboot tdd
Skill mturac/everything-openai-codex/docs/zh-CN/skills/springboot-tdd
EOC: open-source operating system for OpenAI Codex workflows with agents, skills, hooks, rules, memory, safety gates, and cross-harness adapters.
npx -y skills add mturac/everything-openai-codex --skill springboot-tddAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
使用JUnit 5、Mockito、MockMvc、Testcontainers和JaCoCo进行Spring Boot的测试驱动开发。适用于添加功能、修复错误或重构时。
SKILL.md
3.7 KB, as published. Nobody here has run it
Spring Boot TDD 工作流程
适用于 Spring Boot 服务、覆盖率 80%+(单元 + 集成)的 TDD 指南。
何时使用
- 新功能或端点
- 错误修复或重构
- 添加数据访问逻辑或安全规则
工作流程
- 先写测试(它们应该失败)
- 实现最小代码以通过测试
- 在测试通过后进行重构
- 强制覆盖率(JaCoCo)
单元测试 (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());
}
}
模式:
- Arrange-Act-Assert
- 避免部分模拟;优先使用显式桩
- 使用
@ParameterizedTest处理变体
Web 层测试 (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());
}
}
集成测试 (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());
}
}
持久层测试 (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
- 对 Postgres/Redis 使用可复用的容器以镜像生产环境
- 通过
@DynamicPropertySource连接,将 JDBC URL 注入 Spring 上下文
覆盖率 (JaCoCo)
Maven 片段:
<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>
断言
- 为可读性,优先使用 AssertJ (
assertThat) - 对于 JSON 响应,使用
jsonPath - 对于异常:
assertThatThrownBy(...)
测试数据构建器
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 命令
- Maven:
mvn -T 4 test或mvn verify - Gradle:
./gradlew test jacocoTestReport
记住:保持测试快速、隔离且确定。测试行为,而非实现细节。