Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Bucle de verificación para proyectos Spring Boot: build, análisis estático, pruebas con cobertura, escaneos de seguridad y revisión de diff antes del lanzamiento o PR.
.claude/skills/affaan-m-springboot-verification/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 41% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 14% | 0% |
| case-20 | ✓→✗ | ▼ Worse | 426% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 13% | 0% |
| case-07 | ✓→✓ | = Same ✓ | 45% | 0% |
在提交 PR 前、重大变更后以及部署前运行。
bashmvn -T 4 clean verify -DskipTests # or ./gradlew clean assemble -x test
如果构建失败,停止并修复。
Maven(常用插件):
bashmvn -T 4 spotbugs:check pmd:check checkstyle:check
Gradle(如果已配置):
bash./gradlew checkstyleMain pmdMain spotbugsMain
bashmvn -T 4 test mvn jacoco:report # verify 80%+ coverage # or ./gradlew test jacocoTestReport
报告:
使用模拟的依赖项来隔离测试服务逻辑:
java@ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; @Test void createUser_validInput_returnsUser() { var dto = new CreateUserDto("Alice", "alice@example.com"); var expected = new User(1L, "Alice", "alice@example.com"); when(userRepository.save(any(User.class))).thenReturn(expected); var result = userService.create(dto); assertThat(result.name()).isEqualTo("Alice"); verify(userRepository).save(any(User.class)); } @Test void createUser_duplicateEmail_throwsException() { var dto = new CreateUserDto("Alice", "existing@example.com"); when(userRepository.existsByEmail(dto.email())).thenReturn(true); assertThatThrownBy(() -> userService.create(dto)) .isInstanceOf(DuplicateEmailException.class); } }
针对真实数据库(而非 H2)进行测试:
java@SpringBootTest @Testcontainers class UserRepositoryIntegrationTest { @Container static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine") .withDatabaseName("testdb"); @DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", postgres::getJdbcUrl); registry.add("spring.datasource.username", postgres::getUsername); registry.add("spring.datasource.password", postgres::getPassword); } @Autowired private UserRepository userRepository; @Test void findByEmail_existingUser_returnsUser() { userRepository.save(new User("Alice", "alice@example.com")); var found = userRepository.findByEmail("alice@example.com"); assertThat(found).isPresent(); assertThat(found.get().getName()).isEqualTo("Alice"); } }
在完整的 Spring 上下文中测试控制器层:
java@WebMvcTest(UserController.class) class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test void createUser_validInput_returns201() throws Exception { var user = new UserDto(1L, "Alice", "alice@example.com"); when(userService.create(any())).thenReturn(user); mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content(""" {"name": "Alice", "email": "alice@example.com"} """)) .andExpect(status().isCreated()) .andExpect(jsonPath("$.name").value("Alice")); } @Test void createUser_invalidEmail_returns400() throws Exception { mockMvc.perform(post("/api/users") .contentType(MediaType.APPLICATION_JSON) .content(""" {"name": "Alice", "email": "not-an-email"} """)) .andExpect(status().isBadRequest()); } }
bash# Dependency CVEs mvn org.owasp:dependency-check-maven:check # or ./gradlew dependencyCheckAnalyze # Secrets in source grep -rn "password\s*=\s*\"" src/ --include="*.java" --include="*.yml" --include="*.properties" grep -rn "sk-\|api_key\|secret" src/ --include="*.java" --include="*.yml" # Secrets (git history) git secrets --scan # if configured
# 检查 System.out.println(应使用日志记录器)
grep -rn "System\.out\.print" src/main/ --include="*.java"
# 检查响应中的原始异常消息
grep -rn "e\.getMessage()" src/main/ --include="*.java"
# 检查通配符 CORS 配置
grep -rn "allowedOrigins.*\*" src/main/ --include="*.java"bashmvn spotless:apply # if using Spotless plugin ./gradlew spotlessApply
bashgit diff --stat git diff
检查清单:
System.out、log.debug 没有防护)验证报告
===================
构建: [通过/失败]
静态分析: [通过/失败] (spotbugs/pmd/checkstyle)
测试: [通过/失败] (X/Y 通过, Z% 覆盖率)
安全性: [通过/失败] (CVE 发现数: N)
差异: [X 个文件变更]
总体: [就绪 / 未就绪]
待修复问题:
1. ...
2. ...mvn -T 4 test + spotbugs 以获取快速反馈记住:快速反馈胜过意外惊喜。保持关卡严格——将警告视为生产系统中的缺陷。
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 19,733 | 12,388 | -37% | 1 | 1 | 0% | 3,550 | 3,349 | -6% | 0 | 0 | — |
case-02 | fail→fail | 36,267 | 14,997 | -59% | 1 | 1 | 0% | 3,225 | 4,473 | +39% | 0 | 0 | — |
case-03 | fail→fail | 20,896 | 10,137 | -51% | 1 | 1 | 0% | 698 | 2,124 | +204% | 0 | 0 | — |
case-04 | fail→fail | 8,323 | 6,155 | -26% | 1 | 1 | 0% | 1,518 | 2,585 | +70% | 0 | 0 | — |
case-05 | fail→pass | 7,613 | 2,644 | -65% | 1 | 1 | 0% | 1,442 | 2,027 | +41% | 0 | 0 | — |
case-06 | pass→pass | 13,804 | 6,413 | -54% | 1 | 1 | 0% | 2,404 | 2,723 | +13% | 0 | 0 | — |
case-07 | pass→pass | 12,150 | 9,767 | -20% | 1 | 1 | 0% | 2,407 | 3,488 | +45% | 0 | 0 | — |
case-08 | pass→pass | 13,625 | 14,383 | +6% | 1 | 1 | 0% | 2,650 | 3,636 | +37% | 0 | 0 | — |
case-09 | fail→fail | 11,968 | 10,210 | -15% | 1 | 1 | 0% | 2,409 | 3,167 | +31% | 0 | 0 | — |
case-10 | pass→pass | 6,213 | 1,894 | -70% | 1 | 1 | 0% | 1,050 | 1,895 | +80% | 0 | 0 | — |
case-11 | pass→pass | 15,424 | 16,365 | +6% | 1 | 1 | 0% | 2,995 | 4,664 | +56% | 0 | 0 | — |
case-12 | pass→pass | 14,005 | 14,265 | +2% | 1 | 1 | 0% | 2,827 | 4,105 | +45% | 0 | 0 | — |
case-13 | pass→pass | 7,942 | 1,854 | -77% | 1 | 1 | 0% | 1,611 | 1,809 | +12% | 0 | 0 | — |
case-14 | pass→pass | 7,075 | 1,491 | -79% | 1 | 1 | 0% | 1,242 | 1,791 | +44% | 0 | 0 | — |
case-15 | pass→pass | 14,947 | 10,020 | -33% | 1 | 1 | 0% | 2,378 | 3,335 | +40% | 0 | 0 | — |
case-16 | pass→pass | 4,074 | 1,942 | -52% | 1 | 1 | 0% | 668 | 1,781 | +167% | 0 | 0 | — |
case-17 | pass→pass | 7,605 | 2,836 | -63% | 1 | 1 | 0% | 1,184 | 1,911 | +61% | 0 | 0 | — |
case-18 | pass→pass | 6,344 | 4,658 | -27% | 1 | 1 | 0% | 1,090 | 2,287 | +110% | 0 | 0 | — |
case-19 | fail→pass | 24,136 | 8,969 | -63% | 1 | 1 | 0% | 2,852 | 3,243 | +14% | 0 | 0 | — |
case-20 | pass→fail | 3,282 | 3,281 | -0% | 1 | 1 | 0% | 382 | 2,009 | +426% | 0 | 0 | — |
case-21 | pass→pass | 5,014 | 6,231 | +24% | 1 | 1 | 0% | 938 | 2,690 | +187% | 0 | 0 | — |
case-22 | pass→pass | 8,427 | 7,981 | -5% | 1 | 1 | 0% | 1,607 | 3,063 | +91% | 0 | 0 | — |
case-23 | pass→pass | 8,654 | 8,541 | -1% | 1 | 1 | 0% | 1,745 | 3,167 | +81% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +4 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.