@xulthekl/team-flow 0.33.0 → 0.34.0
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/.claude/always/phase-guard.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/marketplace.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/plugin/marketplace.json +2 -2
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +35 -0
- package/CONTRIBUTING.md +44 -0
- package/GEMINI.md +1 -1
- package/INSTALL.md +1 -1
- package/README.md +1 -1
- package/docs/README_en.md +1 -1
- package/docs/solutions/INDEX.md +1 -0
- package/docs/solutions/cross-phase/2026-08-04-no-summary.md +17 -0
- package/gemini-extension.json +1 -1
- package/hooks/session-start +2 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/lib/conventions-generator.mjs +350 -0
- package/scripts/lib/test-record.mjs +65 -2
- package/skills/test-strategy/SKILL.md +37 -0
- package/skills/test-strategy/references/integration-test-contracts.md +237 -0
- package/skills/test-strategy/references/integration-test-isolation.md +346 -0
- package/skills/test-strategy/references/test-quality-rules.md +292 -0
- package/skills/workflow-bootstrap/SKILL.md +40 -3
- package/templates/agent-template.md +41 -0
- package/templates/conventions/_manifest.json +39 -0
- package/templates/conventions/glaf4-compliant/java-testing.md +367 -0
- package/templates/conventions/glaf4-compliant/spring-patterns.md +415 -0
- package/templates/conventions/js-testing.md +261 -0
- package/templates/conventions/python-testing.md +333 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# Integration Test Isolation(集成测试隔离分级)
|
|
2
|
+
|
|
3
|
+
> 来源:glaf4-test h2-rabbitmq-redis.md 的隔离策略(v0.13 §59)
|
|
4
|
+
> 用途:集成测试的分层策略,write-integration-worker 参考
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 一、核心原则
|
|
9
|
+
|
|
10
|
+
**测试隔离**:每个测试独立运行,不依赖其他测试的状态,不污染共享资源。
|
|
11
|
+
|
|
12
|
+
**隔离级别**:
|
|
13
|
+
- **L1:进程内隔离**(最严格):H2、embedded-redis、rabbitmq-mock
|
|
14
|
+
- **L2:事务隔离**:@Transactional 自动回滚
|
|
15
|
+
- **L3:手动清理**:@AfterEach 手动清理资源
|
|
16
|
+
- **L4:无隔离**(最宽松):共享真实基础设施(仅限开发环境)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 二、基础设施隔离策略
|
|
21
|
+
|
|
22
|
+
### 1. 数据库(MySQL/PostgreSQL)
|
|
23
|
+
|
|
24
|
+
| 策略 | 适用场景 | 实现方式 | 优缺点 |
|
|
25
|
+
|------|---------|---------|--------|
|
|
26
|
+
| **H2 内存库** | Repository 层测试 | `@DataJpaTest` + H2 依赖 | ✅ 快速、隔离 ❌ SQL 兼容性差异 |
|
|
27
|
+
| **@Transactional** | Service 层测试 | 测试类加 `@Transactional` | ✅ 自动回滚 ❌ 异步操作不适用 |
|
|
28
|
+
| **Testcontainers** | 需要真实数据库 | Docker 容器 | ✅ 真实环境 ❌ 慢、需要 Docker |
|
|
29
|
+
|
|
30
|
+
**推荐**:
|
|
31
|
+
- Repository 层:H2 内存库
|
|
32
|
+
- Service 层:@Transactional(同步操作)
|
|
33
|
+
- API 层:@Transactional 或 Testcontainers
|
|
34
|
+
|
|
35
|
+
**H2 配置示例**:
|
|
36
|
+
```yaml
|
|
37
|
+
# application-test.yml
|
|
38
|
+
spring:
|
|
39
|
+
datasource:
|
|
40
|
+
url: jdbc:h2:mem:testdb;MODE=MySQL;DATABASE_TO_UPPER=false
|
|
41
|
+
driver-class-name: org.h2.Driver
|
|
42
|
+
jpa:
|
|
43
|
+
hibernate:
|
|
44
|
+
ddl-auto: create-drop
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. 消息队列(RabbitMQ/Kafka)
|
|
48
|
+
|
|
49
|
+
| 策略 | 适用场景 | 实现方式 | 优缺点 |
|
|
50
|
+
|------|---------|---------|--------|
|
|
51
|
+
| **rabbitmq-mock** | MQ 生产者测试 | `MockConnectionFactory` | ✅ 快速、隔离 ❌ 不测试真实 MQ |
|
|
52
|
+
| **embedded broker** | MQ 消费者测试 | embedded-rabbitmq | ✅ 真实协议 ❌ 慢、资源占用 |
|
|
53
|
+
| **Testcontainers** | 端到端测试 | Docker 容器 | ✅ 真实环境 ❌ 慢、需要 Docker |
|
|
54
|
+
|
|
55
|
+
**推荐**:
|
|
56
|
+
- 生产者:rabbitmq-mock
|
|
57
|
+
- 消费者:embedded broker 或 Testcontainers
|
|
58
|
+
|
|
59
|
+
**rabbitmq-mock 配置示例**:
|
|
60
|
+
```java
|
|
61
|
+
@Configuration
|
|
62
|
+
public class RabbitMockConfig {
|
|
63
|
+
@Bean
|
|
64
|
+
public ConnectionFactory connectionFactory() {
|
|
65
|
+
return new MockConnectionFactory();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 3. 缓存(Redis)
|
|
71
|
+
|
|
72
|
+
| 策略 | 适用场景 | 实现方式 | 优缺点 |
|
|
73
|
+
|------|---------|---------|--------|
|
|
74
|
+
| **embedded-redis** | 缓存测试 | embedded-redis(port 6378) | ✅ 真实协议 ❌ ARM64 兼容问题 |
|
|
75
|
+
| **mock** | 简单缓存逻辑 | Mockito mock | ✅ 快速 ❌ 不测试真实 Redis |
|
|
76
|
+
| **Testcontainers** | 端到端测试 | Docker 容器 | ✅ 真实环境 ❌ 慢、需要 Docker |
|
|
77
|
+
|
|
78
|
+
**推荐**:
|
|
79
|
+
- 简单缓存逻辑:mock
|
|
80
|
+
- 复杂缓存逻辑:embedded-redis
|
|
81
|
+
|
|
82
|
+
**embedded-redis 配置示例**:
|
|
83
|
+
```java
|
|
84
|
+
@Configuration
|
|
85
|
+
public class RedisTestConfig {
|
|
86
|
+
@Bean
|
|
87
|
+
public RedisServer redisServer() throws IOException {
|
|
88
|
+
RedisServer server = new RedisServer(6378);
|
|
89
|
+
server.start();
|
|
90
|
+
return server;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 4. 外部 HTTP(第三方 API)
|
|
96
|
+
|
|
97
|
+
| 策略 | 适用场景 | 实现方式 | 优缺点 |
|
|
98
|
+
|------|---------|---------|--------|
|
|
99
|
+
| **MockWebServer** | HTTP 客户端测试 | OkHttp MockWebServer | ✅ 真实 HTTP 协议 ❌ 只测试客户端 |
|
|
100
|
+
| **WireMock** | 复杂 API 模拟 | WireMock | ✅ 强大的模拟能力 ❌ 学习成本 |
|
|
101
|
+
| **@MockBean** | Spring Cloud Feign | Mockito mock | ✅ 简单 ❌ 不测试真实 HTTP |
|
|
102
|
+
|
|
103
|
+
**推荐**:
|
|
104
|
+
- 简单 HTTP 客户端:MockWebServer
|
|
105
|
+
- 复杂 API 模拟:WireMock
|
|
106
|
+
- Feign 客户端:@MockBean
|
|
107
|
+
|
|
108
|
+
**MockWebServer 配置示例**:
|
|
109
|
+
```java
|
|
110
|
+
@Test
|
|
111
|
+
void testCallExternalApi() throws Exception {
|
|
112
|
+
MockWebServer server = new MockWebServer();
|
|
113
|
+
server.enqueue(new MockResponse()
|
|
114
|
+
.setBody("{\"status\":\"ok\"}")
|
|
115
|
+
.setHeader("Content-Type", "application/json"));
|
|
116
|
+
|
|
117
|
+
// 调用被测方法
|
|
118
|
+
String result = externalService.callApi();
|
|
119
|
+
|
|
120
|
+
// 验证请求
|
|
121
|
+
RecordedRequest request = server.takeRequest();
|
|
122
|
+
assertEquals("/api/data", request.getPath());
|
|
123
|
+
assertEquals("ok", result);
|
|
124
|
+
|
|
125
|
+
server.shutdown();
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 三、分层隔离策略
|
|
132
|
+
|
|
133
|
+
### Repository 层
|
|
134
|
+
|
|
135
|
+
**策略**:H2 内存库 + @Transactional
|
|
136
|
+
|
|
137
|
+
**理由**:
|
|
138
|
+
- Repository 层只测试 SQL 和映射逻辑
|
|
139
|
+
- H2 内存库提供快速、隔离的测试环境
|
|
140
|
+
- @Transactional 确保测试后数据回滚
|
|
141
|
+
|
|
142
|
+
**示例**:
|
|
143
|
+
```java
|
|
144
|
+
@DataJpaTest
|
|
145
|
+
@Transactional
|
|
146
|
+
class UserRepositoryTest {
|
|
147
|
+
@Autowired
|
|
148
|
+
private UserRepository userRepository;
|
|
149
|
+
|
|
150
|
+
@Test
|
|
151
|
+
void testFindByEmail() {
|
|
152
|
+
// Arrange
|
|
153
|
+
User user = new User("John", "john@example.com");
|
|
154
|
+
userRepository.save(user);
|
|
155
|
+
|
|
156
|
+
// Act
|
|
157
|
+
Optional<User> found = userRepository.findByEmail("john@example.com");
|
|
158
|
+
|
|
159
|
+
// Assert
|
|
160
|
+
assertTrue(found.isPresent());
|
|
161
|
+
assertEquals("John", found.get().getName());
|
|
162
|
+
}
|
|
163
|
+
// 测试结束后自动回滚
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Service 层
|
|
168
|
+
|
|
169
|
+
**策略**:@Transactional(同步操作)或手动清理(异步操作)
|
|
170
|
+
|
|
171
|
+
**理由**:
|
|
172
|
+
- Service 层测试业务逻辑,需要真实依赖
|
|
173
|
+
- 同步操作用 @Transactional 自动回滚
|
|
174
|
+
- 异步操作需要手动清理(消息队列、缓存)
|
|
175
|
+
|
|
176
|
+
**示例(同步)**:
|
|
177
|
+
```java
|
|
178
|
+
@SpringBootTest
|
|
179
|
+
@Transactional
|
|
180
|
+
class UserServiceTest {
|
|
181
|
+
@Autowired
|
|
182
|
+
private UserService userService;
|
|
183
|
+
|
|
184
|
+
@Autowired
|
|
185
|
+
private UserRepository userRepository;
|
|
186
|
+
|
|
187
|
+
@Test
|
|
188
|
+
void testCreateUser() {
|
|
189
|
+
// Arrange
|
|
190
|
+
CreateUserRequest request = new CreateUserRequest("John", "john@example.com");
|
|
191
|
+
|
|
192
|
+
// Act
|
|
193
|
+
User user = userService.createUser(request);
|
|
194
|
+
|
|
195
|
+
// Assert
|
|
196
|
+
assertNotNull(user.getId());
|
|
197
|
+
assertEquals("John", user.getName());
|
|
198
|
+
}
|
|
199
|
+
// 测试结束后自动回滚
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**示例(异步)**:
|
|
204
|
+
```java
|
|
205
|
+
@SpringBootTest
|
|
206
|
+
class UserServiceAsyncTest {
|
|
207
|
+
@Autowired
|
|
208
|
+
private UserService userService;
|
|
209
|
+
|
|
210
|
+
@Autowired
|
|
211
|
+
private RabbitTemplate rabbitTemplate;
|
|
212
|
+
|
|
213
|
+
@AfterEach
|
|
214
|
+
void cleanup() {
|
|
215
|
+
// 手动清空消息队列
|
|
216
|
+
rabbitTemplate.execute(channel -> {
|
|
217
|
+
channel.queuePurge("user-events");
|
|
218
|
+
return null;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@Test
|
|
223
|
+
void testCreateUserSendsEvent() {
|
|
224
|
+
// Arrange
|
|
225
|
+
CreateUserRequest request = new CreateUserRequest("John", "john@example.com");
|
|
226
|
+
|
|
227
|
+
// Act
|
|
228
|
+
User user = userService.createUser(request);
|
|
229
|
+
|
|
230
|
+
// Assert
|
|
231
|
+
assertNotNull(user.getId());
|
|
232
|
+
|
|
233
|
+
// 验证消息发送
|
|
234
|
+
Message message = rabbitTemplate.receive("user-events", 1000);
|
|
235
|
+
assertNotNull(message);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### API 层
|
|
241
|
+
|
|
242
|
+
**策略**:MockMvc + @Transactional 或 Testcontainers
|
|
243
|
+
|
|
244
|
+
**理由**:
|
|
245
|
+
- API 层测试 HTTP 请求和响应
|
|
246
|
+
- 使用 MockMvc 模拟 HTTP 请求
|
|
247
|
+
- 数据库用 @Transactional 或 Testcontainers
|
|
248
|
+
|
|
249
|
+
**示例**:
|
|
250
|
+
```java
|
|
251
|
+
@WebMvcTest(UserController.class)
|
|
252
|
+
@Transactional
|
|
253
|
+
class UserControllerTest {
|
|
254
|
+
@Autowired
|
|
255
|
+
private MockMvc mockMvc;
|
|
256
|
+
|
|
257
|
+
@MockBean
|
|
258
|
+
private UserService userService;
|
|
259
|
+
|
|
260
|
+
@Test
|
|
261
|
+
void testCreateUser() throws Exception {
|
|
262
|
+
// Arrange
|
|
263
|
+
User user = new User(1L, "John", "john@example.com");
|
|
264
|
+
when(userService.createUser(any())).thenReturn(user);
|
|
265
|
+
|
|
266
|
+
// Act & Assert
|
|
267
|
+
mockMvc.perform(post("/api/users")
|
|
268
|
+
.contentType(MediaType.APPLICATION_JSON)
|
|
269
|
+
.content("{\"name\":\"John\",\"email\":\"john@example.com\"}"))
|
|
270
|
+
.andExpect(status().isOk())
|
|
271
|
+
.andExpect(jsonPath("$.name").value("John"));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## 四、隔离级别选择指南
|
|
279
|
+
|
|
280
|
+
| 测试类型 | 隔离级别 | 推荐策略 |
|
|
281
|
+
|---------|---------|---------|
|
|
282
|
+
| **Repository 层** | L1 | H2 内存库 |
|
|
283
|
+
| **Service 层(同步)** | L2 | @Transactional |
|
|
284
|
+
| **Service 层(异步)** | L3 | 手动清理 |
|
|
285
|
+
| **API 层(简单)** | L2 | MockMvc + @Transactional |
|
|
286
|
+
| **API 层(复杂)** | L1 | MockMvc + H2 |
|
|
287
|
+
| **端到端** | L1 | Testcontainers |
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 五、常见问题和解决方案
|
|
292
|
+
|
|
293
|
+
### 1. 异步操作测试
|
|
294
|
+
|
|
295
|
+
**问题**:@Transactional 不适用于异步操作(消息队列、异步方法)
|
|
296
|
+
|
|
297
|
+
**解决方案**:
|
|
298
|
+
- 使用 @AfterEach 手动清理
|
|
299
|
+
- 使用 Awaitility 等待异步操作完成
|
|
300
|
+
- 使用 embedded broker 替代 mock
|
|
301
|
+
|
|
302
|
+
### 2. 外部依赖测试
|
|
303
|
+
|
|
304
|
+
**问题**:外部 API 不可用或不稳定
|
|
305
|
+
|
|
306
|
+
**解决方案**:
|
|
307
|
+
- 使用 MockWebServer/WireMock 模拟外部 API
|
|
308
|
+
- 使用 @MockBean mock Feign 客户端
|
|
309
|
+
- 使用 Testcontainers 运行真实服务
|
|
310
|
+
|
|
311
|
+
### 3. 测试数据污染
|
|
312
|
+
|
|
313
|
+
**问题**:测试数据泄漏到其他测试
|
|
314
|
+
|
|
315
|
+
**解决方案**:
|
|
316
|
+
- 每个测试独立数据(Builder 模式)
|
|
317
|
+
- 使用 @Transactional 自动回滚
|
|
318
|
+
- 使用 @DirtContexts 标记需要重新加载上下文的测试
|
|
319
|
+
|
|
320
|
+
### 4. 测试速度慢
|
|
321
|
+
|
|
322
|
+
**问题**:Testcontainers 启动慢
|
|
323
|
+
|
|
324
|
+
**解决方案**:
|
|
325
|
+
- 使用 @Testcontainers 的 reuse 模式
|
|
326
|
+
- 使用 H2 替代真实数据库(Repository 层)
|
|
327
|
+
- 使用 mock 替代真实外部服务
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## 六、与 glaf4-test 的关系
|
|
332
|
+
|
|
333
|
+
本隔离策略参考了 glaf4-test 的以下规范:
|
|
334
|
+
- **H2 替代 MySQL**:`MERGE INTO ... KEY(id)` 幂等写法、`MODE=MySQL;DATABASE_TO_UPPER=false`
|
|
335
|
+
- **rabbitmq-mock**:`MockConnectionFactory → CachingConnectionFactory → firstRabbitTemplate`
|
|
336
|
+
- **embedded-redis**:端口 6378、`@AutoConfigureBefore` Redisson config
|
|
337
|
+
|
|
338
|
+
**注意**:glaf4-test 的部分配置是 GLAF4 框架专用的(如 `gtmc.glaf4.*` 配置键),本规范已抽象为通用模式。
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## 变更记录
|
|
343
|
+
|
|
344
|
+
| 日期 | 版本 | 变更内容 |
|
|
345
|
+
|------|------|---------|
|
|
346
|
+
| 2026-08-04 | v1.0 | 从 glaf4-test h2-rabbitmq-redis.md 抽取通用隔离策略 |
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Test Quality Rules(测试质量规则)
|
|
2
|
+
|
|
3
|
+
> 来源:glaf4-test scan-generated-tests.py 的通用规则(v0.13 §56)
|
|
4
|
+
> 用途:code-reviewer 在测试审查时参考,build-executor 在生成测试后自检
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 一、断言质量规则
|
|
9
|
+
|
|
10
|
+
### 1. missing-meaningful-assertion
|
|
11
|
+
|
|
12
|
+
**描述**:测试没有有效断言
|
|
13
|
+
|
|
14
|
+
**检查**:
|
|
15
|
+
- 测试方法中没有 `assert*`、`verify*`、`expect*` 等断言语句
|
|
16
|
+
- 只有被测方法调用,没有验证结果
|
|
17
|
+
|
|
18
|
+
**示例(违规)**:
|
|
19
|
+
```java
|
|
20
|
+
@Test
|
|
21
|
+
void testGetUser() {
|
|
22
|
+
userService.getUser(1L); // 没有断言
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**修正**:
|
|
27
|
+
```java
|
|
28
|
+
@Test
|
|
29
|
+
void testGetUser() {
|
|
30
|
+
User user = userService.getUser(1L);
|
|
31
|
+
assertNotNull(user);
|
|
32
|
+
assertEquals("John", user.getName());
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. weak-assertion-only
|
|
37
|
+
|
|
38
|
+
**描述**:只有弱断言(assertNotNull/isNotNull)
|
|
39
|
+
|
|
40
|
+
**检查**:
|
|
41
|
+
- 只有 `assertNotNull`、`assertThat(x).isNotNull()`
|
|
42
|
+
- 没有验证具体属性值
|
|
43
|
+
|
|
44
|
+
**示例(违规)**:
|
|
45
|
+
```java
|
|
46
|
+
@Test
|
|
47
|
+
void testGetUser() {
|
|
48
|
+
User user = userService.getUser(1L);
|
|
49
|
+
assertNotNull(user); // 只验证非空,不验证内容
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**修正**:
|
|
54
|
+
```java
|
|
55
|
+
@Test
|
|
56
|
+
void testGetUser() {
|
|
57
|
+
User user = userService.getUser(1L);
|
|
58
|
+
assertNotNull(user);
|
|
59
|
+
assertEquals("John", user.getName());
|
|
60
|
+
assertEquals(25, user.getAge());
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 3. verify-only-without-assertion
|
|
65
|
+
|
|
66
|
+
**描述**:只有 mock verify,没有状态断言
|
|
67
|
+
|
|
68
|
+
**检查**:
|
|
69
|
+
- 只有 `verify(mock).method()`
|
|
70
|
+
- 没有验证返回值或状态变化
|
|
71
|
+
|
|
72
|
+
**示例(违规)**:
|
|
73
|
+
```java
|
|
74
|
+
@Test
|
|
75
|
+
void testCreateUser() {
|
|
76
|
+
userService.createUser("John");
|
|
77
|
+
verify(userRepository).save(any()); // 只验证调用,不验证结果
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**修正**:
|
|
82
|
+
```java
|
|
83
|
+
@Test
|
|
84
|
+
void testCreateUser() {
|
|
85
|
+
User user = userService.createUser("John");
|
|
86
|
+
assertNotNull(user);
|
|
87
|
+
assertEquals("John", user.getName());
|
|
88
|
+
verify(userRepository).save(any());
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 二、调试代码残留规则
|
|
95
|
+
|
|
96
|
+
### 4. system-out / print-stack-trace
|
|
97
|
+
|
|
98
|
+
**描述**:测试中有调试代码残留
|
|
99
|
+
|
|
100
|
+
**检查**:
|
|
101
|
+
- `System.out.println`
|
|
102
|
+
- `System.err.println`
|
|
103
|
+
- `e.printStackTrace()`
|
|
104
|
+
- `console.log`(JavaScript)
|
|
105
|
+
- `print()`(Python,非断言)
|
|
106
|
+
|
|
107
|
+
**示例(违规)**:
|
|
108
|
+
```java
|
|
109
|
+
@Test
|
|
110
|
+
void testGetUser() {
|
|
111
|
+
User user = userService.getUser(1L);
|
|
112
|
+
System.out.println("User: " + user); // 调试代码
|
|
113
|
+
assertNotNull(user);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**修正**:
|
|
118
|
+
```java
|
|
119
|
+
@Test
|
|
120
|
+
void testGetUser() {
|
|
121
|
+
User user = userService.getUser(1L);
|
|
122
|
+
assertNotNull(user);
|
|
123
|
+
// 如需调试,使用日志框架或断点
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 三、测试状态规则
|
|
130
|
+
|
|
131
|
+
### 5. disabled-test / unfinished-test-todo
|
|
132
|
+
|
|
133
|
+
**描述**:禁用或未完成的测试
|
|
134
|
+
|
|
135
|
+
**检查**:
|
|
136
|
+
- `@Disabled`、`@Ignore`
|
|
137
|
+
- `// TODO`、`// FIXME`
|
|
138
|
+
- `pending`、`skip`(JavaScript/Python)
|
|
139
|
+
|
|
140
|
+
**处理**:
|
|
141
|
+
- 禁用的测试必须有注释说明原因
|
|
142
|
+
- 未完成的测试不应提交到主分支
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 四、测试数据规则
|
|
147
|
+
|
|
148
|
+
### 6. hardcoded-sample-like-value
|
|
149
|
+
|
|
150
|
+
**描述**:检测到样本数据
|
|
151
|
+
|
|
152
|
+
**检查**:
|
|
153
|
+
- 手机号:`13xxxxxxxxx`、`15xxxxxxxxx`
|
|
154
|
+
- 身份证:`110101199001011234`
|
|
155
|
+
- 邮箱:`test@example.com`、`xxx@test.com`
|
|
156
|
+
- 姓名:`张三`、`李四`、`John`、`Doe`
|
|
157
|
+
- 地址:`北京市朝阳区xxx`
|
|
158
|
+
- 数字:`123456`、`111111`、`000000`
|
|
159
|
+
|
|
160
|
+
**处理**:
|
|
161
|
+
- 使用有意义的测试数据(如业务相关的名称)
|
|
162
|
+
- 或使用测试数据工厂(Builder/Fixture)
|
|
163
|
+
|
|
164
|
+
### 7. real-external-url
|
|
165
|
+
|
|
166
|
+
**描述**:测试中包含真实外部 URL
|
|
167
|
+
|
|
168
|
+
**检查**:
|
|
169
|
+
- `http://api.example.com`
|
|
170
|
+
- `https://third-party-service.com`
|
|
171
|
+
- 任何非 localhost/127.0.0.1 的 URL
|
|
172
|
+
|
|
173
|
+
**处理**:
|
|
174
|
+
- 使用 mock/stub 替代真实外部服务
|
|
175
|
+
- 或使用 `@MockBean` / `WireMock`
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## 五、测试结构规则
|
|
180
|
+
|
|
181
|
+
### 8. large-test-class
|
|
182
|
+
|
|
183
|
+
**描述**:测试类过大
|
|
184
|
+
|
|
185
|
+
**检查**:
|
|
186
|
+
- 测试方法数 > 20
|
|
187
|
+
- 测试类行数 > 500
|
|
188
|
+
|
|
189
|
+
**处理**:
|
|
190
|
+
- 按功能拆分测试类
|
|
191
|
+
- 使用 `@Nested` 分组
|
|
192
|
+
|
|
193
|
+
### 9. generic-test-class-name
|
|
194
|
+
|
|
195
|
+
**描述**:类名过于泛化
|
|
196
|
+
|
|
197
|
+
**检查**:
|
|
198
|
+
- `Test`、`Tests`、`TestSuite`
|
|
199
|
+
- `ServiceTest`、`ControllerTest`(无具体功能描述)
|
|
200
|
+
|
|
201
|
+
**示例(违规)**:
|
|
202
|
+
```java
|
|
203
|
+
class ServiceTest { ... }
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**修正**:
|
|
207
|
+
```java
|
|
208
|
+
class UserServiceGetUserTest { ... }
|
|
209
|
+
class UserServiceCreateUserTest { ... }
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 六、矩阵对账规则
|
|
215
|
+
|
|
216
|
+
### 10. case-method-not-found
|
|
217
|
+
|
|
218
|
+
**描述**:矩阵中的 case 引用了不存在的测试方法
|
|
219
|
+
|
|
220
|
+
**检查**:
|
|
221
|
+
- test-matrix.md 中声明的 `test_method_name` 在测试代码中不存在
|
|
222
|
+
|
|
223
|
+
### 11. case-test-file-mismatch
|
|
224
|
+
|
|
225
|
+
**描述**:矩阵中的 case 与测试文件不匹配
|
|
226
|
+
|
|
227
|
+
**检查**:
|
|
228
|
+
- case 声明的 `test_tier` 与实际测试文件不符(如 unit case 写到了集成测试文件)
|
|
229
|
+
|
|
230
|
+
### 12. evidence-not-in-method
|
|
231
|
+
|
|
232
|
+
**描述**:矩阵中的 evidence 在测试方法中找不到
|
|
233
|
+
|
|
234
|
+
**检查**:
|
|
235
|
+
- case 声明的 `design_method` 对应的代码模式在测试方法中不存在
|
|
236
|
+
|
|
237
|
+
### 13. extra-test-methods
|
|
238
|
+
|
|
239
|
+
**描述**:测试文件中有未在矩阵中声明的方法
|
|
240
|
+
|
|
241
|
+
**检查**:
|
|
242
|
+
- 测试文件中的方法数 > 矩阵中的 case 数
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 七、语言特定规则
|
|
247
|
+
|
|
248
|
+
### 14. Java 特定
|
|
249
|
+
|
|
250
|
+
- 禁止 `throws Exception`(应显式捕获和断言异常)
|
|
251
|
+
- 禁止 `System.out.println` 替代断言
|
|
252
|
+
- 单元测试禁止 `@SpringBootTest`
|
|
253
|
+
- 集成测试禁止只 mock 不验证
|
|
254
|
+
|
|
255
|
+
### 15. JavaScript 特定
|
|
256
|
+
|
|
257
|
+
- 禁止 `console.log` 替代断言
|
|
258
|
+
- 禁止 `expect(true).toBe(true)`(无意义断言)
|
|
259
|
+
- async 测试必须 await
|
|
260
|
+
|
|
261
|
+
### 16. Python 特定
|
|
262
|
+
|
|
263
|
+
- 禁止 `print()` 替代断言
|
|
264
|
+
- 禁止 `assert True`(无意义断言)
|
|
265
|
+
- 测试函数必须以 `test_` 开头
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 八、使用方式
|
|
270
|
+
|
|
271
|
+
### build-executor 自检
|
|
272
|
+
|
|
273
|
+
implementer 子代理生成测试后,按此规则自检:
|
|
274
|
+
1. 检查断言质量(规则 1-3)
|
|
275
|
+
2. 检查调试代码残留(规则 4)
|
|
276
|
+
3. 检查测试数据(规则 6-7)
|
|
277
|
+
4. 检查测试结构(规则 8-9)
|
|
278
|
+
|
|
279
|
+
### code-reviewer 审查
|
|
280
|
+
|
|
281
|
+
code-reviewer 在审查测试代码时,按此规则检查:
|
|
282
|
+
1. 矩阵对账(规则 10-13)
|
|
283
|
+
2. 语言特定规则(规则 14-16)
|
|
284
|
+
3. 测试状态(规则 5)
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 变更记录
|
|
289
|
+
|
|
290
|
+
| 日期 | 版本 | 变更内容 |
|
|
291
|
+
|------|------|---------|
|
|
292
|
+
| 2026-08-04 | v1.0 | 从 glaf4-test scan-generated-tests.py 抽取 15 条通用规则 |
|
|
@@ -62,6 +62,43 @@ Do NOT invoke for:
|
|
|
62
62
|
|
|
63
63
|
详细流程(脚本采集项、子代理维度表、侦察内容树、已有文档处理)见 `references/b1-reconnaissance.md`。
|
|
64
64
|
|
|
65
|
+
### B1.5: Conventions Generator(测试规范生成,v0.34.0 新增)
|
|
66
|
+
|
|
67
|
+
> 来源:test-capability-enhancement-design v1.0 §四
|
|
68
|
+
> 目的:根据项目技术栈自动生成测试规范 conventions,符合 glaf4-test 要求
|
|
69
|
+
|
|
70
|
+
**触发条件**:B1 侦察完成后,检查 `.team-flow/conventions/` 是否存在
|
|
71
|
+
|
|
72
|
+
**存量项目路径**:
|
|
73
|
+
1. 读取 B1 侦察结果中的技术栈信息(从 baseline.md 提取)
|
|
74
|
+
2. 根据技术栈选择 conventions 模板
|
|
75
|
+
3. 复制到项目 `.team-flow/conventions/` 目录
|
|
76
|
+
4. 更新 `.team-flow/conventions/.versions.json`
|
|
77
|
+
5. 更新 `team-flow.config.json` 的 conventions 字段
|
|
78
|
+
|
|
79
|
+
**全新项目路径**(未检测到技术栈特征):
|
|
80
|
+
1. 交互式引导用户选择技术栈
|
|
81
|
+
- 编程语言:Java / JavaScript / Python
|
|
82
|
+
- 构建工具:Maven / Gradle / npm / yarn / pip / poetry
|
|
83
|
+
- 测试框架:JUnit 5 + Mockito / Jest / Pytest
|
|
84
|
+
- 应用框架:Spring Boot / Express / Django / 无框架
|
|
85
|
+
2. 根据用户选择选择 conventions 模板
|
|
86
|
+
3. (可选)生成项目骨架(pom.xml / package.json / requirements.txt)
|
|
87
|
+
4. 生成 conventions 文件
|
|
88
|
+
|
|
89
|
+
**conventions 模板来源**:
|
|
90
|
+
- plugin 内置模板:`${CLAUDE_PLUGIN_ROOT}/templates/conventions/`
|
|
91
|
+
- 符合 glaf4-test 要求的模板:`${CLAUDE_PLUGIN_ROOT}/templates/conventions/glaf4-compliant/`
|
|
92
|
+
|
|
93
|
+
**产出**:
|
|
94
|
+
- `.team-flow/conventions/*.md` — 测试规范文件
|
|
95
|
+
- `.team-flow/conventions/.versions.json` — 版本信息
|
|
96
|
+
- `.team-flow/team-flow.config.json` — conventions 路径映射(更新)
|
|
97
|
+
|
|
98
|
+
**跳过条件**:
|
|
99
|
+
- `.team-flow/conventions/` 已存在 → 提示用户"conventions 已存在,是否更新?"
|
|
100
|
+
- 用户拒绝 → 跳过
|
|
101
|
+
|
|
65
102
|
### B2: Architecture Baseline(架构基线文档化)
|
|
66
103
|
|
|
67
104
|
**条件**:如果 B1 侦察发现项目有一定复杂度(≥5 个模块或 ≥10 个源文件),执行此阶段。否则跳过。
|
|
@@ -173,10 +210,10 @@ Step 2: 确定性提取(兜底)
|
|
|
173
210
|
## Output Standard
|
|
174
211
|
|
|
175
212
|
每次交互结束时说明:
|
|
176
|
-
1. 已完成的阶段(B1-B5)
|
|
177
|
-
2. 已产出的制品(baseline.md / ARCHITECTURE.md / PHYSICAL-MODEL.md / schema-baseline.sql / API-INDEX.md / INDEX.md / CONCEPTS.md / 目录结构)
|
|
213
|
+
1. 已完成的阶段(B1-B1.5-B2-B3-B4-B5)
|
|
214
|
+
2. 已产出的制品(baseline.md / conventions / ARCHITECTURE.md / PHYSICAL-MODEL.md / schema-baseline.sql / API-INDEX.md / INDEX.md / CONCEPTS.md / 目录结构)
|
|
178
215
|
3. 下一步建议(调用哪个 skill)
|
|
179
216
|
|
|
180
217
|
## Success Output
|
|
181
218
|
|
|
182
|
-
接入结束时输出:侦察模式、产出清单(baseline.md / ARCHITECTURE.md / DATABASE.md / PHYSICAL-MODEL.md / schema-baseline.sql / API-INDEX.md / INDEX.md / docs/architecture/CONCEPTS.md / 目录结构)、关键发现(技术栈/模块数/架构模式/测试覆盖)、下一步建议。完整模板见 `references/b1-reconnaissance.md`「Success Output 模板」。
|
|
219
|
+
接入结束时输出:侦察模式、产出清单(baseline.md / conventions / ARCHITECTURE.md / DATABASE.md / PHYSICAL-MODEL.md / schema-baseline.sql / API-INDEX.md / INDEX.md / docs/architecture/CONCEPTS.md / 目录结构)、关键发现(技术栈/模块数/架构模式/测试覆盖)、下一步建议。完整模板见 `references/b1-reconnaissance.md`「Success Output 模板」。
|