kld-sdd 2.6.0 → 2.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +1 -1
- package/skywalk-sdd/index.cjs +1 -1
- package/skywalk-sdd/ontology/id.cjs +16 -19
- package/templates/openspec/proposal.md +1 -1
- package/templates/skills/kld-sdd/opsx-apply/SKILL.md +35 -5
- package/templates/skills/kld-sdd/opsx-apply/checklist.md +43 -9
- package/templates/skills/kld-sdd/opsx-apply/implementer-prompt.md +50 -3
- package/templates/skills/kld-sdd/opsx-apply/reference.md +13 -18
- package/templates/skills/kld-sdd/opsx-check/SKILL.md +16 -4
- package/templates/skills/kld-sdd/opsx-check/checklist.md +11 -1
- package/templates/skills/kld-sdd/opsx-design/SKILL.md +2 -0
- package/templates/skills/kld-sdd/opsx-design/checklist.md +1 -0
- package/templates/skills/kld-sdd/opsx-propose/reference.md +8 -16
- package/templates/skills/kld-sdd/opsx-rules/reference.md +1 -1
- package/templates/skills/kld-sdd/opsx-spec/SKILL.md +2 -0
- package/templates/skills/kld-sdd/opsx-task/SKILL.md +41 -25
- package/templates/skills/kld-sdd/opsx-task/checklist.md +9 -0
- package/templates/skills/kld-sdd/opsx-task/reference.md +79 -2
- package/templates/skills/kld-sdd/opsx-tdd-anti-patterns/SKILL.md +79 -0
- package/templates/skills/kld-sdd/opsx-tdd-anti-patterns/reference.md +203 -0
- package/templates/skills/kld-sdd/opsx-tdd-core/SKILL.md +167 -0
- package/templates/skills/kld-sdd/opsx-tdd-core/checklist.md +55 -0
- package/templates/skills/kld-sdd/opsx-tdd-core/reference.md +146 -0
- package/templates/skills/kld-sdd/opsx-tdd-metrics/SKILL.md +73 -0
- package/templates/skills/kld-sdd/opsx-tdd-metrics/checklist.md +60 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/SKILL.md +95 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/cause-effect-clarity.md +19 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/clean-test-data.md +33 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/existing-test-awareness.md +17 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/given-when-then.md +44 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/good-test-qualities.md +32 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/mock-boundary.md +44 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/naming-conventions.md +37 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/no-logic-in-tests.md +30 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/one-test-one-scenario.md +23 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/parameterized-testing.md +56 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/prefer-public-apis.md +17 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/general/test-behaviors-not-methods.md +26 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/java/argument-matching.md +38 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/java/controller-test-rules.md +37 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/java/domain-service-rules.md +33 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/java/java-test-template.md +42 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/java/json-serialization.md +34 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/java/logging-rules.md +35 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/post-generation/compilation-verification.md +25 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/post-generation/execution-verification.md +28 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/python/py-test-template.md +40 -0
- package/templates/skills/kld-sdd/opsx-tdd-quality/rules/typescript/ts-test-template.md +45 -0
- package/templates/skills/kld-sdd/opsx-tdd-review/SKILL.md +66 -0
- package/templates/skills/kld-sdd/opsx-tdd-review/checklist.md +39 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/SKILL.md +29 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/controller-strategy.md +32 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/dag-generation-rules.md +20 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/des-step-annotation.md +36 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/exception-path-coverage.md +47 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/green-scope-declaration.md +45 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/green-yagni-fence.md +41 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/multi-validation-split.md +36 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/non-tdd-modules.md +17 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/refactor-checklist.md +45 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/task-type-definitions.md +23 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/tdd-strategy-selection.md +13 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/test-execution-gate.md +25 -0
- package/templates/skills/kld-sdd/opsx-tdd-rules/rules/test-skeleton-telemetry.md +19 -0
- package/templates/skills/kld-sdd/opsx-test/SKILL.md +18 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# 测试中不要放逻辑
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 核心原则
|
|
6
|
+
|
|
7
|
+
**KISS > DRY** — 在测试中,简单性比避免重复更重要。
|
|
8
|
+
|
|
9
|
+
## 禁止的模式
|
|
10
|
+
|
|
11
|
+
- 循环:`for (int i = 0; i < users.size(); i++) { assertThat(...) }`
|
|
12
|
+
- 条件:`if (response.isSuccessful()) { assertThat(...) }`
|
|
13
|
+
- 字符串拼接:`assertThat(result).isEqualTo("Hello, " + userName + "!")`
|
|
14
|
+
- 计算:`assertThat(total).isEqualTo(price * quantity + tax)`
|
|
15
|
+
|
|
16
|
+
## 正确做法
|
|
17
|
+
|
|
18
|
+
- 使用字面值——`assertThat(result).isEqualTo("Hello, John!")`
|
|
19
|
+
- 预计算的期望值——`int expectedTotal = 115;`
|
|
20
|
+
- 用 `assertThat(users).extracting(User::isActive).containsOnly(true)` 代替循环
|
|
21
|
+
|
|
22
|
+
## 逻辑隐藏 Bug 的经典案例
|
|
23
|
+
|
|
24
|
+
```java
|
|
25
|
+
// 错误:字符串拼接隐藏了 bug(结果为 "//u/0/photos")
|
|
26
|
+
assertThat(photosPageUrl).isEqualTo(baseUrl + "/u/0/photos");
|
|
27
|
+
|
|
28
|
+
// 正确:字面值让 bug 显而易见
|
|
29
|
+
assertThat(actualUrl).isEqualTo("http://photos.google.com/u/0/photos");
|
|
30
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# 一测一场景
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 核心规则
|
|
6
|
+
|
|
7
|
+
每个测试只验证一个特定场景。多个场景混在一个测试中会导致失败难以诊断。
|
|
8
|
+
|
|
9
|
+
## 反模式信号
|
|
10
|
+
|
|
11
|
+
- 测试名包含 "and"(如 `testDepositAndWithdraw`)
|
|
12
|
+
- 多个 "When" 或 "Act" 段
|
|
13
|
+
- 断言之间有状态变化
|
|
14
|
+
- 难以简洁命名
|
|
15
|
+
- 测试超过 10-15 行
|
|
16
|
+
|
|
17
|
+
## 多断言何时可以
|
|
18
|
+
|
|
19
|
+
当验证**同一个行为**的多个属性时可以。例如验证用户创建行为时,同时断言 id 不为 null、email 正确、name 正确、createdAt 不为 null——这些都是验证"用户创建"这一个行为。
|
|
20
|
+
|
|
21
|
+
## 拆分判断标准
|
|
22
|
+
|
|
23
|
+
问自己"如果这个测试失败,我能确切知道哪个场景坏了吗?"如果不能,就拆分。
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# 参数化测试指导
|
|
2
|
+
|
|
3
|
+
> 影响等级:MEDIUM
|
|
4
|
+
> 引用方:opsx-tdd-quality/SKILL.md §7
|
|
5
|
+
|
|
6
|
+
## 适用场景
|
|
7
|
+
|
|
8
|
+
参数化测试适用于**同一行为、不同输入组合**的场景,减少重复代码。
|
|
9
|
+
|
|
10
|
+
## 何时使用
|
|
11
|
+
|
|
12
|
+
✅ **适合参数化**:
|
|
13
|
+
- 同一方法的多个输入-输出组合(如不同角色的登录测试)
|
|
14
|
+
- 边界值测试(如分页参数 page=0/1/100)
|
|
15
|
+
- 枚举值覆盖(如 status=borrowed/returned)
|
|
16
|
+
|
|
17
|
+
❌ **不适合参数化**:
|
|
18
|
+
- 不同行为点(如 create 和 list 应分开,不用参数化)
|
|
19
|
+
- 异常路径与正常路径混合(断言类型不同)
|
|
20
|
+
- 测试间有状态依赖
|
|
21
|
+
|
|
22
|
+
## Java 示例(JUnit 5)
|
|
23
|
+
|
|
24
|
+
```java
|
|
25
|
+
@ParameterizedTest
|
|
26
|
+
@CsvSource({
|
|
27
|
+
"admin, admin, true",
|
|
28
|
+
"user, user, true",
|
|
29
|
+
"admin, user, false",
|
|
30
|
+
"user, admin, false"
|
|
31
|
+
})
|
|
32
|
+
void login_roleMatching_returnsCorrectResult(String userRole, String requestRole, boolean shouldSucceed) {
|
|
33
|
+
// Given
|
|
34
|
+
User user = new User();
|
|
35
|
+
user.setRole(userRole);
|
|
36
|
+
when(userRepository.findByUsername(any())).thenReturn(Optional.of(user));
|
|
37
|
+
|
|
38
|
+
if (shouldSucceed) {
|
|
39
|
+
// When
|
|
40
|
+
LoginResponse response = authService.login("test", "Pass1234", requestRole);
|
|
41
|
+
// Then
|
|
42
|
+
assertNotNull(response);
|
|
43
|
+
} else {
|
|
44
|
+
// When & Then
|
|
45
|
+
assertThrows(BusinessException.class,
|
|
46
|
+
() -> authService.login("test", "Pass1234", requestRole));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 注意事项
|
|
52
|
+
|
|
53
|
+
- 参数化测试仍需遵循 Given-When-Then 结构
|
|
54
|
+
- 每个参数组合应能独立理解
|
|
55
|
+
- 参数化测试不替代独立的行为点测试
|
|
56
|
+
- 参数行中避免逻辑(如 `if` 判断应放在测试方法内,不在参数中编码逻辑)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# 测试行为而非方法
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 核心思想
|
|
6
|
+
|
|
7
|
+
围绕行为(系统做什么)组织测试,而非围绕方法(怎么实现)。
|
|
8
|
+
|
|
9
|
+
## 识别行为
|
|
10
|
+
|
|
11
|
+
问"这个操作的可观察效果是什么?"
|
|
12
|
+
|
|
13
|
+
以 `resetPassword()` 为例:
|
|
14
|
+
- 用户密码被清空 → 独立测试
|
|
15
|
+
- 用户收到邮件 → 独立测试
|
|
16
|
+
- 重置计数器递增 → 独立测试
|
|
17
|
+
|
|
18
|
+
## 一个行为可以有多个断言
|
|
19
|
+
|
|
20
|
+
验证邮件通知行为时,可以同时断言收件人、标题、正文——都是关于"邮件"这一个行为。
|
|
21
|
+
|
|
22
|
+
## 命名模式
|
|
23
|
+
|
|
24
|
+
以行为命名而非方法名:
|
|
25
|
+
- ✅ `resetPassword_clearsPassword`
|
|
26
|
+
- ❌ `testResetPassword`
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Mockito 参数匹配
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 核心规则
|
|
6
|
+
|
|
7
|
+
- **禁止**对 DTO/模型对象使用 `any(...)` 进行 stub 或 verify
|
|
8
|
+
- 必须用 `ArgumentCaptor` 捕获真实参数并断言相关字段
|
|
9
|
+
|
|
10
|
+
## `any()` 可接受的场景
|
|
11
|
+
|
|
12
|
+
- 原始类型且精确值不影响测试
|
|
13
|
+
- 简单类型(String、Integer)且焦点在其他行为
|
|
14
|
+
- 仅验证方法被调用(存在性检查)
|
|
15
|
+
|
|
16
|
+
## ArgumentCaptor 最佳实践
|
|
17
|
+
|
|
18
|
+
```java
|
|
19
|
+
@Captor
|
|
20
|
+
private ArgumentCaptor<Order> orderCaptor;
|
|
21
|
+
|
|
22
|
+
@Test
|
|
23
|
+
void create_validInput_savesOrder() {
|
|
24
|
+
// When
|
|
25
|
+
orderService.create(orderDTO);
|
|
26
|
+
// Then
|
|
27
|
+
verify(repository).save(orderCaptor.capture());
|
|
28
|
+
Order saved = orderCaptor.getValue();
|
|
29
|
+
assertThat(saved.getName()).isEqualTo("Test Order");
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 验证多次调用
|
|
34
|
+
|
|
35
|
+
```java
|
|
36
|
+
verify(repository, times(2)).save(captor.capture());
|
|
37
|
+
List<Order> saved = captor.getAllValues();
|
|
38
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# 控制器测试规则
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 核心规则
|
|
6
|
+
|
|
7
|
+
使用 `@WebMvcTest` 进行隔离的 web 层测试。**禁止**使用 `@SpringBootTest` 做控制器单元测试。
|
|
8
|
+
|
|
9
|
+
## 关键注解
|
|
10
|
+
|
|
11
|
+
| 注解 | 用途 |
|
|
12
|
+
|------|------|
|
|
13
|
+
| `@WebMvcTest(Controller.class)` | 只加载指定控制器的 web 层 |
|
|
14
|
+
| `@MockitoBean` | 创建 Mockito mock 并注册到 Spring 上下文(Spring Boot 3.4+) |
|
|
15
|
+
| `@MockBean` | Spring Boot < 3.4 的替代 |
|
|
16
|
+
| `@Autowired MockMvc` | 注入 MockMvc 实例 |
|
|
17
|
+
|
|
18
|
+
## 控制器测试范围
|
|
19
|
+
|
|
20
|
+
1. 请求映射:正确的 URL、HTTP 方法、Content-Type
|
|
21
|
+
2. 请求验证:`@Valid`/`@Validated` 触发验证
|
|
22
|
+
3. 响应状态码:200、201、400、401、403、404 等
|
|
23
|
+
4. 响应体:通过 `jsonPath()` 断言 JSON 结构
|
|
24
|
+
5. 路径变量和查询参数:正确绑定
|
|
25
|
+
6. 异常处理:`@ControllerAdvice`/`@ExceptionHandler` 响应
|
|
26
|
+
|
|
27
|
+
## 安全注解测试
|
|
28
|
+
|
|
29
|
+
```java
|
|
30
|
+
@Test
|
|
31
|
+
@WithMockUser(roles = "ADMIN")
|
|
32
|
+
void getUsers_asAdmin_returns200() throws Exception { ... }
|
|
33
|
+
|
|
34
|
+
@Test
|
|
35
|
+
@WithMockUser(roles = "USER")
|
|
36
|
+
void getUsers_asUser_returns403() throws Exception { ... }
|
|
37
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# 领域/服务单元测试规则
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 核心规则
|
|
6
|
+
|
|
7
|
+
- 使用 `@ExtendWith(MockitoExtension.class)` 管理协作者
|
|
8
|
+
- **禁止**为单元测试启动框架或容器
|
|
9
|
+
- Mock 外部依赖,**不 mock 被测系统**
|
|
10
|
+
- **绝不 mock 简单值对象**
|
|
11
|
+
|
|
12
|
+
## Mock 边界
|
|
13
|
+
|
|
14
|
+
| 应该 Mock | 用真实对象 |
|
|
15
|
+
|-----------|-----------|
|
|
16
|
+
| Repository / DAO | DTO / 值对象 |
|
|
17
|
+
| 外部服务客户端 | 领域实体(大多数情况) |
|
|
18
|
+
| 消息生产者 | 工具类 |
|
|
19
|
+
| 缓存服务 | Mapper(通常) |
|
|
20
|
+
| 任何 I/O 操作 | |
|
|
21
|
+
|
|
22
|
+
## 验证模式
|
|
23
|
+
|
|
24
|
+
```java
|
|
25
|
+
// 验证未调用
|
|
26
|
+
verify(service, never()).send(any());
|
|
27
|
+
|
|
28
|
+
// 验证调用次数
|
|
29
|
+
verify(repository, times(2)).findById(anyString());
|
|
30
|
+
|
|
31
|
+
// 验证无更多交互
|
|
32
|
+
verifyNoMoreInteractions(paymentService);
|
|
33
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Java 测试模板
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 技术栈
|
|
6
|
+
|
|
7
|
+
JUnit 5 + Mockito + AssertJ
|
|
8
|
+
|
|
9
|
+
## 禁止
|
|
10
|
+
|
|
11
|
+
**禁止**在单元测试中使用 `@SpringBootTest`(除非特定测试类型模板明确要求)
|
|
12
|
+
|
|
13
|
+
## 标准模板
|
|
14
|
+
|
|
15
|
+
```java
|
|
16
|
+
@ExtendWith(MockitoExtension.class)
|
|
17
|
+
class CalculatorServiceTest {
|
|
18
|
+
@Mock
|
|
19
|
+
private DependencyService dependencyService;
|
|
20
|
+
@InjectMocks
|
|
21
|
+
private CalculatorService calculatorService;
|
|
22
|
+
|
|
23
|
+
@Test
|
|
24
|
+
void calculate_validInput_returnsResult() {
|
|
25
|
+
// Given
|
|
26
|
+
when(dependencyService.getValue()).thenReturn(10);
|
|
27
|
+
// When
|
|
28
|
+
int actualResult = calculatorService.calculate(5);
|
|
29
|
+
// Then
|
|
30
|
+
int expectedResult = 15;
|
|
31
|
+
assertThat(actualResult).isEqualTo(expectedResult);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 关键点
|
|
37
|
+
|
|
38
|
+
1. 测试类与 SUT 放在同一包中
|
|
39
|
+
2. 用 `@ExtendWith(MockitoExtension.class)` mock 依赖
|
|
40
|
+
3. `@Mock` 标注依赖,`@InjectMocks` 标注 SUT
|
|
41
|
+
4. 遵循 Given-When-Then 模式并加注释
|
|
42
|
+
5. 使用 AssertJ 断言(`assertThat()`)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# JSON 序列化
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 核心规则
|
|
6
|
+
|
|
7
|
+
- **禁止**在测试中调用运行时序列化器(`objectMapper.writeValueAsString`、`gson.toJson` 等)
|
|
8
|
+
- **必须**使用显式 JSON 字符串字面量
|
|
9
|
+
|
|
10
|
+
## 正例
|
|
11
|
+
|
|
12
|
+
```java
|
|
13
|
+
String requestJson = """
|
|
14
|
+
{
|
|
15
|
+
"name": "John",
|
|
16
|
+
"email": "john@test.com"
|
|
17
|
+
}
|
|
18
|
+
""";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 断言方式
|
|
22
|
+
|
|
23
|
+
用 `jsonPath()` 验证响应 JSON 的具体字段,而非比较整个序列化字符串:
|
|
24
|
+
|
|
25
|
+
```java
|
|
26
|
+
.andExpect(jsonPath("$.name").value("John"))
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 好处
|
|
30
|
+
|
|
31
|
+
- 可读性(期望数据直接可见)
|
|
32
|
+
- 确定性(不依赖序列化器配置)
|
|
33
|
+
- 可调试性
|
|
34
|
+
- 可维护性
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# 日志输出验证
|
|
2
|
+
|
|
3
|
+
> 影响等级:MEDIUM
|
|
4
|
+
|
|
5
|
+
## 核心规则
|
|
6
|
+
|
|
7
|
+
使用 `@ExtendWith(OutputCaptureExtension.class)` 捕获和验证日志输出。
|
|
8
|
+
|
|
9
|
+
## 反模式
|
|
10
|
+
|
|
11
|
+
手动 `System.setOut(new PrintStream(...))` — 脆弱且容易忘记重置。
|
|
12
|
+
|
|
13
|
+
## 正确模式
|
|
14
|
+
|
|
15
|
+
```java
|
|
16
|
+
@ExtendWith(OutputCaptureExtension.class)
|
|
17
|
+
class OrderServiceTest {
|
|
18
|
+
@Test
|
|
19
|
+
void processOrder_success_logsOrderId(CapturedOutput output) {
|
|
20
|
+
var order = new Order("order-123", "product-1");
|
|
21
|
+
orderService.processOrder(order);
|
|
22
|
+
assertThat(output.getOut()).contains("Processing order: order-123");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## CapturedOutput 方法
|
|
28
|
+
|
|
29
|
+
- `getOut()` — stdout
|
|
30
|
+
- `getErr()` — stderr
|
|
31
|
+
- `getAll()` — 全部
|
|
32
|
+
|
|
33
|
+
## 重要说明
|
|
34
|
+
|
|
35
|
+
`OutputCaptureExtension` 来自 `spring-boot-test` 依赖,但**不启动 Spring 上下文**——只捕获 `System.out`/`System.err`,完全兼容单元测试。
|
package/templates/skills/kld-sdd/opsx-tdd-quality/rules/post-generation/compilation-verification.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# 编译验证
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 编译命令
|
|
6
|
+
|
|
7
|
+
- Maven:`mvn test-compile -q`
|
|
8
|
+
- Gradle:`gradle testClasses -q`
|
|
9
|
+
- npm/yarn:`npm run build` 或 `npx tsc --noEmit`
|
|
10
|
+
- Python:`python -m py_compile <test_file>`
|
|
11
|
+
|
|
12
|
+
## 流程
|
|
13
|
+
|
|
14
|
+
创建文件 → 运行编译 → 失败则修复(缺导入、缺依赖、语法错误、包路径错误、类型不匹配)→ 重试(**最多 5 次**)
|
|
15
|
+
|
|
16
|
+
## 常见问题
|
|
17
|
+
|
|
18
|
+
- 缺少 import
|
|
19
|
+
- 缺少 Maven/Gradle 依赖
|
|
20
|
+
- 包声明与目录不匹配
|
|
21
|
+
- 类型不匹配(如 Long vs String)
|
|
22
|
+
|
|
23
|
+
## 铁律
|
|
24
|
+
|
|
25
|
+
绝不交付无法编译的测试。
|
package/templates/skills/kld-sdd/opsx-tdd-quality/rules/post-generation/execution-verification.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 执行验证
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 执行命令
|
|
6
|
+
|
|
7
|
+
只运行生成的测试类,不跑全套:
|
|
8
|
+
- Maven:`mvn test -Dtest={TestClassName} -q`
|
|
9
|
+
- Gradle:`gradle test --tests "{fully.qualified.TestClassName}" -q`
|
|
10
|
+
|
|
11
|
+
## 失败处理流程
|
|
12
|
+
|
|
13
|
+
1. 仔细阅读失败输出
|
|
14
|
+
2. 识别根因(错误期望值、mock setup 不正确、缺少 stubbing、方法行为假设错误)
|
|
15
|
+
3. **修复测试——绝不修改生产代码**
|
|
16
|
+
4. 重试验证(**每个失败测试最多 3 次修复尝试**)
|
|
17
|
+
5. 3 次后仍失败:删除该测试方法,加 `// TODO:` 注释说明,告知用户
|
|
18
|
+
|
|
19
|
+
## 常见失败原因
|
|
20
|
+
|
|
21
|
+
- 期望值错误 → 读生产代码理解实际返回值
|
|
22
|
+
- 缺少 mock stubbing → 只 stub 实际调用的方法
|
|
23
|
+
- Mockito 严格 stub 违规(`UnnecessaryStubbingException`)→ 删除不必要的 stub,**不要用 `lenient()`**
|
|
24
|
+
- NPE → 检查 mock 是否默认返回 null,添加正确 stubbing
|
|
25
|
+
|
|
26
|
+
## 铁律
|
|
27
|
+
|
|
28
|
+
绝不交付失败的测试。如果生产代码有 bug,测试应记录**当前行为**并加注释标注疑似 bug。
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Python 测试模板
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 技术栈
|
|
6
|
+
|
|
7
|
+
pytest + unittest.mock
|
|
8
|
+
|
|
9
|
+
## 禁止
|
|
10
|
+
|
|
11
|
+
**禁止**在单元测试中使用全量集成框架(除非特定测试类型模板明确要求)
|
|
12
|
+
|
|
13
|
+
## 标准模板
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import pytest
|
|
17
|
+
from unittest.mock import Mock
|
|
18
|
+
from services.user_service import UserService
|
|
19
|
+
|
|
20
|
+
class TestUserService:
|
|
21
|
+
def setup_method(self):
|
|
22
|
+
self.mock_repo = Mock()
|
|
23
|
+
self.service = UserService(self.mock_repo)
|
|
24
|
+
|
|
25
|
+
def test_create_user_valid_input_returns_user_id(self):
|
|
26
|
+
# Given
|
|
27
|
+
self.mock_repo.save.return_value = Mock(id='user-001')
|
|
28
|
+
# When
|
|
29
|
+
result = self.service.create_user('test', 'Pass1234', 'user')
|
|
30
|
+
# Then
|
|
31
|
+
assert result == 'user-001'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 关键点
|
|
35
|
+
|
|
36
|
+
1. 使用 `pytest` + `unittest.mock.Mock` 管理协作者
|
|
37
|
+
2. Mock 外部依赖,**不 mock 被测系统**
|
|
38
|
+
3. 遵循 Given-When-Then 模式
|
|
39
|
+
4. 测试方法名遵循 `test_{method}_{state}_{outcome}` 格式
|
|
40
|
+
5. 使用 `assert` 断言
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# TypeScript 测试模板
|
|
2
|
+
|
|
3
|
+
> 影响等级:HIGH
|
|
4
|
+
|
|
5
|
+
## 技术栈
|
|
6
|
+
|
|
7
|
+
Vitest / Jest + @testing-library
|
|
8
|
+
|
|
9
|
+
## 禁止
|
|
10
|
+
|
|
11
|
+
**禁止**在单元测试中使用全量集成框架(除非特定测试类型模板明确要求)
|
|
12
|
+
|
|
13
|
+
## 标准模板
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
17
|
+
import { UserService } from './UserService';
|
|
18
|
+
|
|
19
|
+
describe('UserService', () => {
|
|
20
|
+
let userService: UserService;
|
|
21
|
+
let mockRepo: any;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
mockRepo = { findById: vi.fn(), save: vi.fn() };
|
|
25
|
+
userService = new UserService(mockRepo);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('createUser_validInput_returnsUserId', async () => {
|
|
29
|
+
// Given
|
|
30
|
+
mockRepo.save.mockResolvedValue({ id: 'user-001', name: 'test' });
|
|
31
|
+
// When
|
|
32
|
+
const result = await userService.createUser('test', 'Pass1234', 'user');
|
|
33
|
+
// Then
|
|
34
|
+
expect(result).toBe('user-001');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 关键点
|
|
40
|
+
|
|
41
|
+
1. 使用 `describe` + `it` 组织测试
|
|
42
|
+
2. `vi.fn()` mock 外部依赖,不 mock 被测系统
|
|
43
|
+
3. 遵循 Given-When-Then 模式
|
|
44
|
+
4. 使用 `expect` 断言
|
|
45
|
+
5. 测试方法名遵循 `{method}_{state}_{outcome}` 格式
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opsx-tdd-review
|
|
3
|
+
description: "测试审查层 — 从审查者角度检测'测试通过但没测到关键点'。8 项质量检查 + 缺失测试检测 + 测试异味检测。当审查测试代码时引用本技能。"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# opsx-tdd-review — 测试审查层
|
|
7
|
+
|
|
8
|
+
> **定位**:从审查者角度检测"测试通过但没测到关键点"。
|
|
9
|
+
> **参考来源**:code-reviewer Phase 5 + tdd-guide 测试质量审查
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## §1 审查目标
|
|
14
|
+
|
|
15
|
+
区分"覆盖率"和"测试质量" — **测试通过 ≠ 测试正确**。
|
|
16
|
+
|
|
17
|
+
## §2 覆盖率审查
|
|
18
|
+
|
|
19
|
+
| 指标 | 阈值 | 说明 |
|
|
20
|
+
|------|------|------|
|
|
21
|
+
| 行覆盖率 | >80% | 基础要求 |
|
|
22
|
+
| 分支覆盖率 | >75% | 仅行覆盖率不够 |
|
|
23
|
+
| 函数覆盖率 | >90% | |
|
|
24
|
+
| 关键路径覆盖率 | 100% | |
|
|
25
|
+
|
|
26
|
+
> 覆盖率不是唯一指标——100% 覆盖率不等于测试有意义。
|
|
27
|
+
|
|
28
|
+
## §3 测试质量审查(8 项)
|
|
29
|
+
|
|
30
|
+
- [ ] 测试是否真正断言了有意义的行为(而非仅 mock 存在)
|
|
31
|
+
- [ ] 测试是否独立隔离(无测试间依赖)
|
|
32
|
+
- [ ] 测试名称是否清晰描述被测内容
|
|
33
|
+
- [ ] mock 和 stub 是否正确使用(Mock 边界,不 Mock 行为)
|
|
34
|
+
- [ ] 无测试间依赖
|
|
35
|
+
- [ ] 无 sleep 或任意等待
|
|
36
|
+
- [ ] 测试了正常路径 + 错误条件 + 边界值 + 空输入
|
|
37
|
+
- [ ] AAA 模式(Arrange-Act-Assert)
|
|
38
|
+
|
|
39
|
+
## §4 缺失测试检测
|
|
40
|
+
|
|
41
|
+
- [ ] 未测试的错误路径
|
|
42
|
+
- [ ] 缺失的负面测试用例
|
|
43
|
+
- [ ] 未覆盖的边界条件
|
|
44
|
+
- [ ] bug 修复无回归测试
|
|
45
|
+
|
|
46
|
+
## §5 测试异味检测
|
|
47
|
+
|
|
48
|
+
> 完整检测信号见 `opsx-tdd-metrics/SKILL.md` §3,此处仅保留快速参考。
|
|
49
|
+
|
|
50
|
+
| 异味 | 检测信号 | 严重程度 |
|
|
51
|
+
|------|---------|---------|
|
|
52
|
+
| 断言轮盘赌 | 单测试 >5 个断言 | 中 |
|
|
53
|
+
| 缺失断言 | 测试代码中无 `assert` 或 `expect` | 高 |
|
|
54
|
+
| sleep 测试 | 检测 `sleep`/`wait` 使用 | 高 |
|
|
55
|
+
| 条件测试逻辑 | 测试中的 `if` 语句 | 中 |
|
|
56
|
+
| 弱断言 | 只检查 truthy(`assertNotNull` 但不检查值) | 中 |
|
|
57
|
+
|
|
58
|
+
## §6 评分标准
|
|
59
|
+
|
|
60
|
+
| 评级 | 标准 |
|
|
61
|
+
|------|------|
|
|
62
|
+
| PASS | 需明确证据且证据反映真实任务完成 |
|
|
63
|
+
| FAIL | 包括:无证据、证据矛盾、证据表面化、巧合满足 |
|
|
64
|
+
| 不确定 | 举证责任在期望方(默认 FAIL) |
|
|
65
|
+
|
|
66
|
+
> 不给予部分分数。每个断言要么通过要么失败。
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "opsx-tdd-review 自检清单 — 测试质量审查 8 项 + 缺失测试检测 + 测试异味检测。仅在审查测试代码时读取。"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# opsx-tdd-review — 自检清单
|
|
6
|
+
|
|
7
|
+
> 仅在审查测试代码时读取。日常流程见 `SKILL.md`。
|
|
8
|
+
|
|
9
|
+
## §A 测试质量审查(8 项)
|
|
10
|
+
|
|
11
|
+
- [ ] 测试是否真正断言了有意义的行为(而非仅 mock 存在)
|
|
12
|
+
- [ ] 测试是否独立隔离(无测试间依赖)
|
|
13
|
+
- [ ] 测试名称是否清晰描述被测内容(符合 `{method}_{state}_{outcome}`)
|
|
14
|
+
- [ ] mock 和 stub 是否正确使用(Mock 边界,不 Mock 行为,见 `opsx-tdd-quality/SKILL.md` §2)
|
|
15
|
+
- [ ] 无测试间依赖
|
|
16
|
+
- [ ] 无 sleep 或任意等待
|
|
17
|
+
- [ ] 测试了正常路径 + 错误条件 + 边界值 + 空输入
|
|
18
|
+
- [ ] AAA 模式(Arrange-Act-Assert)/ Given-When-Then 结构
|
|
19
|
+
|
|
20
|
+
## §B 缺失测试检测
|
|
21
|
+
|
|
22
|
+
- [ ] 未测试的错误路径(每个 orElseThrow/边界检查有对应测试,见 `opsx-tdd-rules/rules/exception-path-coverage.md`)
|
|
23
|
+
- [ ] 缺失的负面测试用例
|
|
24
|
+
- [ ] 未覆盖的边界条件
|
|
25
|
+
- [ ] bug 修复无回归测试
|
|
26
|
+
|
|
27
|
+
## §C 测试异味检测
|
|
28
|
+
|
|
29
|
+
| 异味 | 检测信号 | 严重程度 |
|
|
30
|
+
|------|---------|---------|
|
|
31
|
+
| 断言轮盘赌 | 单测试 >5 个断言 | 中 |
|
|
32
|
+
| 缺失断言 | 测试代码中无 `assert` 或 `expect` | 高 |
|
|
33
|
+
| sleep 测试 | 检测 `sleep`/`wait` 使用 | 高 |
|
|
34
|
+
| 条件测试逻辑 | 测试中的 `if` 语句 | 中 |
|
|
35
|
+
| 弱断言 | 只检查 truthy(`assertNotNull` 但不检查值) | 中 |
|
|
36
|
+
|
|
37
|
+
## §D 反模式检测
|
|
38
|
+
|
|
39
|
+
- [ ] 已检查 `opsx-tdd-anti-patterns/SKILL.md` §3-§4 的 15 种反模式
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opsx-tdd-rules
|
|
3
|
+
description: "TDD 规则库 — DAG 生成规则、Controller 策略、任务类型定义、telemetry 模板等独立规则文件。当需要具体 TDD 规则文件时引用本技能。"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# opsx-tdd-rules — 规则库
|
|
7
|
+
|
|
8
|
+
> **定位**:TDD 相关的规则文件集合,供其他技能按需引用。
|
|
9
|
+
> **参考来源**:unit-tests-skills rules/ 目录模式
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 规则索引
|
|
14
|
+
|
|
15
|
+
| 规则文件 | 摘要 | 影响等级 | 引用方 |
|
|
16
|
+
|---------|------|---------|--------|
|
|
17
|
+
| `rules/tdd-strategy-selection.md` | TDD 策略选择文案(tdd/impl-first/none 三选一) | HIGH | opsx-task |
|
|
18
|
+
| `rules/dag-generation-rules.md` | DAG 生成规则表(三种策略的 DAG 规则对照) | HIGH | opsx-task |
|
|
19
|
+
| `rules/task-type-definitions.md` | RED/GREEN/REFACTOR/VERIFY 任务类型定义 | HIGH | opsx-task |
|
|
20
|
+
| `rules/non-tdd-modules.md` | 非 TDD 模块排除规则(前端/配置/SQL DDL) | MEDIUM | opsx-task |
|
|
21
|
+
| `rules/controller-strategy.md` | Controller 层策略 A/B | HIGH | opsx-task |
|
|
22
|
+
| `rules/test-skeleton-telemetry.md` | test-skeleton telemetry 命令模板 | MEDIUM | opsx-apply |
|
|
23
|
+
| `rules/test-execution-gate.md` | 测试执行门禁策略(tdd=强制/impl-first=警告/none=跳过) | HIGH | opsx-apply |
|
|
24
|
+
| `rules/green-yagni-fence.md` | GREEN 任务 YAGNI 围栏自动注入规则 | HIGH | opsx-task |
|
|
25
|
+
| `rules/green-scope-declaration.md` | GREEN 任务 Scope 声明步骤(断言清单→流程标记→仅实现属于的步骤) | HIGH | opsx-apply |
|
|
26
|
+
| `rules/des-step-annotation.md` | DES 元素步骤级标注规则(TDD 模式下标注 [GREEN-N] 归属) | MEDIUM | opsx-design |
|
|
27
|
+
| `rules/multi-validation-split.md` | 多校验条件拆分规则(每个校验条件须有独立 AC 场景) | MEDIUM | opsx-spec |
|
|
28
|
+
| `rules/exception-path-coverage.md` | 异常路径测试覆盖门禁(每个 orElseThrow/边界检查须有对应 RED 测试) | HIGH | opsx-task, opsx-apply, opsx-check |
|
|
29
|
+
| `rules/refactor-checklist.md` | REFACTOR 阶段检查点(public API 不变、行为保持、重构质量评估) | HIGH | opsx-apply, opsx-tdd-core |
|