code-abyss 1.6.16 → 1.7.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/package.json +2 -2
- package/skills/SKILL.md +24 -16
- package/skills/domains/ai/SKILL.md +2 -2
- package/skills/domains/ai/prompt-and-eval.md +279 -0
- package/skills/domains/architecture/SKILL.md +2 -3
- package/skills/domains/architecture/security-arch.md +87 -0
- package/skills/domains/data-engineering/SKILL.md +188 -26
- package/skills/domains/development/SKILL.md +1 -4
- package/skills/domains/devops/SKILL.md +3 -5
- package/skills/domains/devops/performance.md +63 -0
- package/skills/domains/devops/testing.md +97 -0
- package/skills/domains/frontend-design/SKILL.md +12 -3
- package/skills/domains/frontend-design/claymorphism/SKILL.md +117 -0
- package/skills/domains/frontend-design/claymorphism/references/tokens.css +52 -0
- package/skills/domains/frontend-design/engineering.md +287 -0
- package/skills/domains/frontend-design/glassmorphism/SKILL.md +138 -0
- package/skills/domains/frontend-design/glassmorphism/references/tokens.css +32 -0
- package/skills/domains/frontend-design/liquid-glass/SKILL.md +135 -0
- package/skills/domains/frontend-design/liquid-glass/references/tokens.css +81 -0
- package/skills/domains/frontend-design/neubrutalism/SKILL.md +141 -0
- package/skills/domains/frontend-design/neubrutalism/references/tokens.css +44 -0
- package/skills/domains/infrastructure/SKILL.md +174 -34
- package/skills/domains/mobile/SKILL.md +211 -21
- package/skills/domains/orchestration/SKILL.md +1 -0
- package/skills/domains/security/SKILL.md +4 -6
- package/skills/domains/security/blue-team.md +57 -0
- package/skills/domains/security/red-team.md +54 -0
- package/skills/domains/security/threat-intel.md +50 -0
- package/skills/orchestration/multi-agent/SKILL.md +195 -46
- package/skills/run_skill.js +134 -0
- package/skills/tools/gen-docs/SKILL.md +6 -4
- package/skills/tools/gen-docs/scripts/doc_generator.js +349 -0
- package/skills/tools/verify-change/SKILL.md +8 -6
- package/skills/tools/verify-change/scripts/change_analyzer.js +270 -0
- package/skills/tools/verify-module/SKILL.md +6 -4
- package/skills/tools/verify-module/scripts/module_scanner.js +145 -0
- package/skills/tools/verify-quality/SKILL.md +5 -3
- package/skills/tools/verify-quality/scripts/quality_checker.js +276 -0
- package/skills/tools/verify-security/SKILL.md +7 -5
- package/skills/tools/verify-security/scripts/security_scanner.js +133 -0
- package/skills/__pycache__/run_skill.cpython-312.pyc +0 -0
- package/skills/domains/COVERAGE_PLAN.md +0 -232
- package/skills/domains/ai/model-evaluation.md +0 -790
- package/skills/domains/ai/prompt-engineering.md +0 -703
- package/skills/domains/architecture/compliance.md +0 -299
- package/skills/domains/architecture/data-security.md +0 -184
- package/skills/domains/data-engineering/data-pipeline.md +0 -762
- package/skills/domains/data-engineering/data-quality.md +0 -894
- package/skills/domains/data-engineering/stream-processing.md +0 -791
- package/skills/domains/development/dart.md +0 -963
- package/skills/domains/development/kotlin.md +0 -834
- package/skills/domains/development/php.md +0 -659
- package/skills/domains/development/swift.md +0 -755
- package/skills/domains/devops/e2e-testing.md +0 -914
- package/skills/domains/devops/performance-testing.md +0 -734
- package/skills/domains/devops/testing-strategy.md +0 -667
- package/skills/domains/frontend-design/build-tools.md +0 -743
- package/skills/domains/frontend-design/performance.md +0 -734
- package/skills/domains/frontend-design/testing.md +0 -699
- package/skills/domains/infrastructure/gitops.md +0 -735
- package/skills/domains/infrastructure/iac.md +0 -855
- package/skills/domains/infrastructure/kubernetes.md +0 -1018
- package/skills/domains/mobile/android-dev.md +0 -979
- package/skills/domains/mobile/cross-platform.md +0 -795
- package/skills/domains/mobile/ios-dev.md +0 -931
- package/skills/domains/security/secrets-management.md +0 -834
- package/skills/domains/security/supply-chain.md +0 -931
- package/skills/domains/security/threat-modeling.md +0 -828
- package/skills/run_skill.py +0 -153
- package/skills/tests/README.md +0 -225
- package/skills/tests/SUMMARY.md +0 -362
- package/skills/tests/__init__.py +0 -3
- package/skills/tests/__pycache__/test_change_analyzer.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_doc_generator.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_module_scanner.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_quality_checker.cpython-312.pyc +0 -0
- package/skills/tests/__pycache__/test_security_scanner.cpython-312.pyc +0 -0
- package/skills/tests/test_change_analyzer.py +0 -558
- package/skills/tests/test_doc_generator.py +0 -538
- package/skills/tests/test_module_scanner.py +0 -376
- package/skills/tests/test_quality_checker.py +0 -516
- package/skills/tests/test_security_scanner.py +0 -426
- package/skills/tools/gen-docs/scripts/__pycache__/doc_generator.cpython-312.pyc +0 -0
- package/skills/tools/gen-docs/scripts/doc_generator.py +0 -520
- package/skills/tools/verify-change/scripts/__pycache__/change_analyzer.cpython-312.pyc +0 -0
- package/skills/tools/verify-change/scripts/change_analyzer.py +0 -529
- package/skills/tools/verify-module/scripts/__pycache__/module_scanner.cpython-312.pyc +0 -0
- package/skills/tools/verify-module/scripts/module_scanner.py +0 -321
- package/skills/tools/verify-quality/scripts/__pycache__/quality_checker.cpython-312.pyc +0 -0
- package/skills/tools/verify-quality/scripts/quality_checker.py +0 -481
- package/skills/tools/verify-security/scripts/__pycache__/security_scanner.cpython-312.pyc +0 -0
- package/skills/tools/verify-security/scripts/security_scanner.py +0 -374
|
@@ -1,667 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: testing-strategy
|
|
3
|
-
description: 测试策略与架构。测试金字塔、测试左移、契约测试、覆盖率策略、测试分层。当用户提到测试策略、测试金字塔、测试左移、契约测试、测试分层、测试覆盖率时使用。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 🎯 测试金字塔 · Testing Strategy
|
|
7
|
-
|
|
8
|
-
## 测试金字塔 (Test Pyramid)
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
/\
|
|
12
|
-
/E2E\ 10% - 慢、脆弱、昂贵
|
|
13
|
-
/------\
|
|
14
|
-
/ 集成 \ 20% - 中速、稳定
|
|
15
|
-
/----------\
|
|
16
|
-
/ 单元 \ 70% - 快、稳定、便宜
|
|
17
|
-
/--------------\
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
### 层级比例
|
|
21
|
-
| 层级 | 占比 | 执行时间 | 成本 | 维护性 |
|
|
22
|
-
|------|------|----------|------|--------|
|
|
23
|
-
| 单元测试 | 70% | <1s | 低 | 高 |
|
|
24
|
-
| 集成测试 | 20% | 1-10s | 中 | 中 |
|
|
25
|
-
| E2E测试 | 10% | 10s-5m | 高 | 低 |
|
|
26
|
-
|
|
27
|
-
### 反模式:冰淇淋锥
|
|
28
|
-
```
|
|
29
|
-
/--------------\
|
|
30
|
-
/ E2E \ 大量 E2E - 慢、不稳定
|
|
31
|
-
/----------------\
|
|
32
|
-
/ 集成 \ 少量集成
|
|
33
|
-
/--------------------\
|
|
34
|
-
/ 单元 \ 极少单元 - 反模式!
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## 测试左移 (Shift-Left Testing)
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
传统流程:
|
|
41
|
-
需求 → 开发 → 测试 → 部署
|
|
42
|
-
↑
|
|
43
|
-
测试介入晚
|
|
44
|
-
|
|
45
|
-
左移流程:
|
|
46
|
-
需求 → 开发 → 部署
|
|
47
|
-
↓ ↓ ↓
|
|
48
|
-
测试 测试 测试
|
|
49
|
-
↑
|
|
50
|
-
测试全程参与
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### 左移实践
|
|
54
|
-
```yaml
|
|
55
|
-
# 需求阶段
|
|
56
|
-
- 可测试性评审
|
|
57
|
-
- 验收标准定义
|
|
58
|
-
- 测试用例设计
|
|
59
|
-
|
|
60
|
-
# 开发阶段
|
|
61
|
-
- TDD (测试驱动开发)
|
|
62
|
-
- 单元测试同步编写
|
|
63
|
-
- 代码审查包含测试
|
|
64
|
-
|
|
65
|
-
# 提交阶段
|
|
66
|
-
- Pre-commit Hook
|
|
67
|
-
- 本地测试必过
|
|
68
|
-
- 静态分析
|
|
69
|
-
|
|
70
|
-
# CI 阶段
|
|
71
|
-
- 自动化测试
|
|
72
|
-
- 覆盖率门禁
|
|
73
|
-
- 性能基准测试
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## 契约测试 (Contract Testing)
|
|
77
|
-
|
|
78
|
-
### 消费者驱动契约 (CDC)
|
|
79
|
-
```
|
|
80
|
-
Provider API ←→ Contract ←→ Consumer
|
|
81
|
-
↓ ↓
|
|
82
|
-
验证契约 验证契约
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Pact 示例
|
|
86
|
-
```javascript
|
|
87
|
-
// Consumer 端
|
|
88
|
-
const { Pact } = require('@pact-foundation/pact');
|
|
89
|
-
|
|
90
|
-
const provider = new Pact({
|
|
91
|
-
consumer: 'UserService',
|
|
92
|
-
provider: 'OrderService'
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe('Order API', () => {
|
|
96
|
-
beforeAll(() => provider.setup());
|
|
97
|
-
afterAll(() => provider.finalize());
|
|
98
|
-
|
|
99
|
-
it('获取订单列表', async () => {
|
|
100
|
-
await provider.addInteraction({
|
|
101
|
-
state: '有3个订单',
|
|
102
|
-
uponReceiving: '获取订单请求',
|
|
103
|
-
withRequest: {
|
|
104
|
-
method: 'GET',
|
|
105
|
-
path: '/orders',
|
|
106
|
-
headers: { Accept: 'application/json' }
|
|
107
|
-
},
|
|
108
|
-
willRespondWith: {
|
|
109
|
-
status: 200,
|
|
110
|
-
headers: { 'Content-Type': 'application/json' },
|
|
111
|
-
body: [
|
|
112
|
-
{ id: 1, status: 'pending' },
|
|
113
|
-
{ id: 2, status: 'completed' }
|
|
114
|
-
]
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const response = await fetch('http://localhost:1234/orders');
|
|
119
|
-
expect(response.status).toBe(200);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Provider 验证
|
|
125
|
-
```javascript
|
|
126
|
-
// Provider 端
|
|
127
|
-
const { Verifier } = require('@pact-foundation/pact');
|
|
128
|
-
|
|
129
|
-
new Verifier({
|
|
130
|
-
provider: 'OrderService',
|
|
131
|
-
providerBaseUrl: 'http://localhost:8080',
|
|
132
|
-
pactUrls: ['./pacts/userservice-orderservice.json'],
|
|
133
|
-
stateHandlers: {
|
|
134
|
-
'有3个订单': async () => {
|
|
135
|
-
// 准备测试数据
|
|
136
|
-
await db.seed(['order1', 'order2', 'order3']);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}).verifyProvider();
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Spring Cloud Contract
|
|
143
|
-
```groovy
|
|
144
|
-
// Contract DSL
|
|
145
|
-
Contract.make {
|
|
146
|
-
request {
|
|
147
|
-
method 'GET'
|
|
148
|
-
url '/api/users/1'
|
|
149
|
-
}
|
|
150
|
-
response {
|
|
151
|
-
status 200
|
|
152
|
-
body([
|
|
153
|
-
id: 1,
|
|
154
|
-
name: 'Alice',
|
|
155
|
-
email: 'alice@example.com'
|
|
156
|
-
])
|
|
157
|
-
headers {
|
|
158
|
-
contentType(applicationJson())
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
```java
|
|
165
|
-
// Provider 测试
|
|
166
|
-
@SpringBootTest
|
|
167
|
-
@AutoConfigureStubRunner(
|
|
168
|
-
ids = "com.example:user-service:+:stubs:8080",
|
|
169
|
-
stubsMode = StubRunnerProperties.StubsMode.LOCAL
|
|
170
|
-
)
|
|
171
|
-
class ContractTest {
|
|
172
|
-
@Test
|
|
173
|
-
void shouldReturnUser() {
|
|
174
|
-
ResponseEntity<User> response = restTemplate
|
|
175
|
-
.getForEntity("http://localhost:8080/api/users/1", User.class);
|
|
176
|
-
|
|
177
|
-
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
|
178
|
-
assertThat(response.getBody().getName()).isEqualTo("Alice");
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## 测试分层策略
|
|
184
|
-
|
|
185
|
-
### 单元测试
|
|
186
|
-
```python
|
|
187
|
-
# 纯函数测试
|
|
188
|
-
def test_calculate_discount():
|
|
189
|
-
assert calculate_discount(100, 0.1) == 90
|
|
190
|
-
assert calculate_discount(100, 0) == 100
|
|
191
|
-
assert calculate_discount(0, 0.5) == 0
|
|
192
|
-
|
|
193
|
-
# Mock 外部依赖
|
|
194
|
-
from unittest.mock import Mock, patch
|
|
195
|
-
|
|
196
|
-
def test_user_service():
|
|
197
|
-
mock_db = Mock()
|
|
198
|
-
mock_db.find_user.return_value = {'id': 1, 'name': 'Alice'}
|
|
199
|
-
|
|
200
|
-
service = UserService(mock_db)
|
|
201
|
-
user = service.get_user(1)
|
|
202
|
-
|
|
203
|
-
assert user['name'] == 'Alice'
|
|
204
|
-
mock_db.find_user.assert_called_once_with(1)
|
|
205
|
-
|
|
206
|
-
# 参数化测试
|
|
207
|
-
import pytest
|
|
208
|
-
|
|
209
|
-
@pytest.mark.parametrize("input,expected", [
|
|
210
|
-
("hello", "HELLO"),
|
|
211
|
-
("World", "WORLD"),
|
|
212
|
-
("", ""),
|
|
213
|
-
])
|
|
214
|
-
def test_uppercase(input, expected):
|
|
215
|
-
assert input.upper() == expected
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
### 集成测试
|
|
219
|
-
```java
|
|
220
|
-
@SpringBootTest
|
|
221
|
-
@Testcontainers
|
|
222
|
-
class OrderServiceIntegrationTest {
|
|
223
|
-
|
|
224
|
-
@Container
|
|
225
|
-
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
|
|
226
|
-
.withDatabaseName("testdb");
|
|
227
|
-
|
|
228
|
-
@Autowired
|
|
229
|
-
private OrderService orderService;
|
|
230
|
-
|
|
231
|
-
@Test
|
|
232
|
-
void shouldCreateOrder() {
|
|
233
|
-
Order order = new Order("user123", List.of("item1", "item2"));
|
|
234
|
-
Order saved = orderService.create(order);
|
|
235
|
-
|
|
236
|
-
assertThat(saved.getId()).isNotNull();
|
|
237
|
-
assertThat(saved.getStatus()).isEqualTo(OrderStatus.PENDING);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
@Test
|
|
241
|
-
void shouldHandleTransaction() {
|
|
242
|
-
assertThrows(InsufficientStockException.class, () -> {
|
|
243
|
-
orderService.createWithInsufficientStock();
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// 验证事务回滚
|
|
247
|
-
assertThat(orderRepository.count()).isEqualTo(0);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
### 组件测试
|
|
253
|
-
```typescript
|
|
254
|
-
// API 组件测试
|
|
255
|
-
import request from 'supertest';
|
|
256
|
-
import { app } from '../src/app';
|
|
257
|
-
|
|
258
|
-
describe('User API', () => {
|
|
259
|
-
it('POST /users - 创建用户', async () => {
|
|
260
|
-
const response = await request(app)
|
|
261
|
-
.post('/users')
|
|
262
|
-
.send({ name: 'Alice', email: 'alice@example.com' })
|
|
263
|
-
.expect(201);
|
|
264
|
-
|
|
265
|
-
expect(response.body).toMatchObject({
|
|
266
|
-
name: 'Alice',
|
|
267
|
-
email: 'alice@example.com'
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it('GET /users/:id - 获取用户', async () => {
|
|
272
|
-
const response = await request(app)
|
|
273
|
-
.get('/users/1')
|
|
274
|
-
.expect(200);
|
|
275
|
-
|
|
276
|
-
expect(response.body.id).toBe(1);
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
## 测试覆盖率策略
|
|
282
|
-
|
|
283
|
-
### 覆盖率类型
|
|
284
|
-
```
|
|
285
|
-
行覆盖率 (Line Coverage) - 代码行是否执行
|
|
286
|
-
分支覆盖率 (Branch Coverage) - 条件分支是否覆盖
|
|
287
|
-
函数覆盖率 (Function Coverage) - 函数是否调用
|
|
288
|
-
语句覆盖率 (Statement Coverage) - 语句是否执行
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### 覆盖率配置
|
|
292
|
-
```javascript
|
|
293
|
-
// Jest 配置
|
|
294
|
-
module.exports = {
|
|
295
|
-
collectCoverage: true,
|
|
296
|
-
coverageThreshold: {
|
|
297
|
-
global: {
|
|
298
|
-
branches: 80,
|
|
299
|
-
functions: 80,
|
|
300
|
-
lines: 80,
|
|
301
|
-
statements: 80
|
|
302
|
-
},
|
|
303
|
-
'./src/core/': {
|
|
304
|
-
branches: 90,
|
|
305
|
-
functions: 95,
|
|
306
|
-
lines: 95,
|
|
307
|
-
statements: 95
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
coveragePathIgnorePatterns: [
|
|
311
|
-
'/node_modules/',
|
|
312
|
-
'/test/',
|
|
313
|
-
'.*\\.config\\.js'
|
|
314
|
-
]
|
|
315
|
-
};
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
```python
|
|
319
|
-
# pytest-cov 配置
|
|
320
|
-
[tool.pytest.ini_options]
|
|
321
|
-
addopts = """
|
|
322
|
-
--cov=src
|
|
323
|
-
--cov-report=html
|
|
324
|
-
--cov-report=term-missing
|
|
325
|
-
--cov-fail-under=80
|
|
326
|
-
"""
|
|
327
|
-
|
|
328
|
-
[tool.coverage.run]
|
|
329
|
-
omit = [
|
|
330
|
-
"*/tests/*",
|
|
331
|
-
"*/migrations/*",
|
|
332
|
-
"*/__init__.py"
|
|
333
|
-
]
|
|
334
|
-
|
|
335
|
-
[tool.coverage.report]
|
|
336
|
-
exclude_lines = [
|
|
337
|
-
"pragma: no cover",
|
|
338
|
-
"def __repr__",
|
|
339
|
-
"raise NotImplementedError",
|
|
340
|
-
"if TYPE_CHECKING:"
|
|
341
|
-
]
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### 覆盖率门禁
|
|
345
|
-
```yaml
|
|
346
|
-
# GitHub Actions
|
|
347
|
-
- name: Test with coverage
|
|
348
|
-
run: npm test -- --coverage
|
|
349
|
-
|
|
350
|
-
- name: Coverage check
|
|
351
|
-
run: |
|
|
352
|
-
COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
|
|
353
|
-
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
|
|
354
|
-
echo "Coverage $COVERAGE% is below 80%"
|
|
355
|
-
exit 1
|
|
356
|
-
fi
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
## TDD (测试驱动开发)
|
|
360
|
-
|
|
361
|
-
### Red-Green-Refactor
|
|
362
|
-
```
|
|
363
|
-
1. Red - 写失败的测试
|
|
364
|
-
2. Green - 写最少代码让测试通过
|
|
365
|
-
3. Refactor - 重构代码
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
### TDD 示例
|
|
369
|
-
```python
|
|
370
|
-
# 1. Red - 写测试
|
|
371
|
-
def test_fizzbuzz():
|
|
372
|
-
assert fizzbuzz(3) == "Fizz"
|
|
373
|
-
assert fizzbuzz(5) == "Buzz"
|
|
374
|
-
assert fizzbuzz(15) == "FizzBuzz"
|
|
375
|
-
assert fizzbuzz(7) == "7"
|
|
376
|
-
|
|
377
|
-
# 2. Green - 实现
|
|
378
|
-
def fizzbuzz(n):
|
|
379
|
-
if n % 15 == 0:
|
|
380
|
-
return "FizzBuzz"
|
|
381
|
-
if n % 3 == 0:
|
|
382
|
-
return "Fizz"
|
|
383
|
-
if n % 5 == 0:
|
|
384
|
-
return "Buzz"
|
|
385
|
-
return str(n)
|
|
386
|
-
|
|
387
|
-
# 3. Refactor - 优化
|
|
388
|
-
def fizzbuzz(n):
|
|
389
|
-
result = ""
|
|
390
|
-
if n % 3 == 0:
|
|
391
|
-
result += "Fizz"
|
|
392
|
-
if n % 5 == 0:
|
|
393
|
-
result += "Buzz"
|
|
394
|
-
return result or str(n)
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
## BDD (行为驱动开发)
|
|
398
|
-
|
|
399
|
-
### Gherkin 语法
|
|
400
|
-
```gherkin
|
|
401
|
-
Feature: 用户登录
|
|
402
|
-
作为一个用户
|
|
403
|
-
我想要登录系统
|
|
404
|
-
以便访问我的账户
|
|
405
|
-
|
|
406
|
-
Scenario: 成功登录
|
|
407
|
-
Given 用户已注册
|
|
408
|
-
When 用户输入正确的用户名和密码
|
|
409
|
-
Then 用户应该看到欢迎页面
|
|
410
|
-
|
|
411
|
-
Scenario: 密码错误
|
|
412
|
-
Given 用户已注册
|
|
413
|
-
When 用户输入错误的密码
|
|
414
|
-
Then 用户应该看到错误提示
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
### Cucumber 实现
|
|
418
|
-
```javascript
|
|
419
|
-
const { Given, When, Then } = require('@cucumber/cucumber');
|
|
420
|
-
const { expect } = require('chai');
|
|
421
|
-
|
|
422
|
-
Given('用户已注册', async function() {
|
|
423
|
-
await this.db.createUser({
|
|
424
|
-
username: 'alice',
|
|
425
|
-
password: 'password123'
|
|
426
|
-
});
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
When('用户输入正确的用户名和密码', async function() {
|
|
430
|
-
this.response = await this.api.login('alice', 'password123');
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
Then('用户应该看到欢迎页面', function() {
|
|
434
|
-
expect(this.response.status).to.equal(200);
|
|
435
|
-
expect(this.response.body.message).to.include('欢迎');
|
|
436
|
-
});
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
## 测试数据管理
|
|
440
|
-
|
|
441
|
-
### Fixture 模式
|
|
442
|
-
```python
|
|
443
|
-
import pytest
|
|
444
|
-
|
|
445
|
-
@pytest.fixture
|
|
446
|
-
def sample_user():
|
|
447
|
-
return {
|
|
448
|
-
'id': 1,
|
|
449
|
-
'name': 'Alice',
|
|
450
|
-
'email': 'alice@example.com'
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
@pytest.fixture
|
|
454
|
-
def db_session():
|
|
455
|
-
session = create_session()
|
|
456
|
-
yield session
|
|
457
|
-
session.rollback()
|
|
458
|
-
session.close()
|
|
459
|
-
|
|
460
|
-
def test_create_user(db_session, sample_user):
|
|
461
|
-
user = User(**sample_user)
|
|
462
|
-
db_session.add(user)
|
|
463
|
-
db_session.commit()
|
|
464
|
-
|
|
465
|
-
assert user.id is not None
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
### Factory 模式
|
|
469
|
-
```javascript
|
|
470
|
-
// Factory Bot
|
|
471
|
-
const { Factory } = require('rosie');
|
|
472
|
-
|
|
473
|
-
Factory.define('user')
|
|
474
|
-
.sequence('id')
|
|
475
|
-
.attr('name', () => faker.name.findName())
|
|
476
|
-
.attr('email', () => faker.internet.email())
|
|
477
|
-
.attr('createdAt', () => new Date());
|
|
478
|
-
|
|
479
|
-
// 使用
|
|
480
|
-
const user = Factory.build('user');
|
|
481
|
-
const users = Factory.buildList('user', 10);
|
|
482
|
-
const savedUser = await Factory.create('user'); // 保存到数据库
|
|
483
|
-
```
|
|
484
|
-
|
|
485
|
-
### 测试数据隔离
|
|
486
|
-
```java
|
|
487
|
-
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
|
488
|
-
class UserServiceTest {
|
|
489
|
-
|
|
490
|
-
@BeforeEach
|
|
491
|
-
void setUp() {
|
|
492
|
-
// 每个测试前清理
|
|
493
|
-
userRepository.deleteAll();
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
@Test
|
|
497
|
-
@Transactional
|
|
498
|
-
void testCreateUser() {
|
|
499
|
-
// 测试结束自动回滚
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
@Test
|
|
503
|
-
@Sql("/test-data/users.sql")
|
|
504
|
-
void testWithSqlData() {
|
|
505
|
-
// 使用 SQL 脚本准备数据
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
## 测试隔离与并行
|
|
511
|
-
|
|
512
|
-
### 并行执行
|
|
513
|
-
```javascript
|
|
514
|
-
// Jest 并行配置
|
|
515
|
-
module.exports = {
|
|
516
|
-
maxWorkers: '50%', // 使用 50% CPU
|
|
517
|
-
testTimeout: 10000,
|
|
518
|
-
bail: 1, // 首次失败即停止
|
|
519
|
-
};
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
```python
|
|
523
|
-
# pytest 并行
|
|
524
|
-
pytest -n auto # 自动检测 CPU 核心数
|
|
525
|
-
pytest -n 4 # 使用 4 个进程
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
### 测试隔离
|
|
529
|
-
```typescript
|
|
530
|
-
describe('Order Service', () => {
|
|
531
|
-
let service: OrderService;
|
|
532
|
-
let mockDb: jest.Mocked<Database>;
|
|
533
|
-
|
|
534
|
-
beforeEach(() => {
|
|
535
|
-
// 每个测试独立实例
|
|
536
|
-
mockDb = createMockDatabase();
|
|
537
|
-
service = new OrderService(mockDb);
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
afterEach(() => {
|
|
541
|
-
jest.clearAllMocks();
|
|
542
|
-
});
|
|
543
|
-
|
|
544
|
-
it('should create order', () => {
|
|
545
|
-
// 测试逻辑
|
|
546
|
-
});
|
|
547
|
-
});
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
## 快照测试
|
|
551
|
-
|
|
552
|
-
### Jest 快照
|
|
553
|
-
```javascript
|
|
554
|
-
test('renders correctly', () => {
|
|
555
|
-
const tree = renderer.create(
|
|
556
|
-
<UserProfile user={{ name: 'Alice', age: 30 }} />
|
|
557
|
-
).toJSON();
|
|
558
|
-
|
|
559
|
-
expect(tree).toMatchSnapshot();
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
// 更新快照
|
|
563
|
-
// npm test -- -u
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
### API 响应快照
|
|
567
|
-
```python
|
|
568
|
-
def test_api_response(snapshot):
|
|
569
|
-
response = client.get('/api/users/1')
|
|
570
|
-
snapshot.assert_match(response.json(), 'user_response.json')
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
## 变异测试 (Mutation Testing)
|
|
574
|
-
|
|
575
|
-
### Stryker 配置
|
|
576
|
-
```javascript
|
|
577
|
-
// stryker.conf.js
|
|
578
|
-
module.exports = {
|
|
579
|
-
mutate: ['src/**/*.ts', '!src/**/*.spec.ts'],
|
|
580
|
-
testRunner: 'jest',
|
|
581
|
-
reporters: ['html', 'clear-text', 'progress'],
|
|
582
|
-
coverageAnalysis: 'perTest',
|
|
583
|
-
thresholds: { high: 80, low: 60, break: 50 }
|
|
584
|
-
};
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
### 变异示例
|
|
588
|
-
```javascript
|
|
589
|
-
// 原始代码
|
|
590
|
-
function isAdult(age) {
|
|
591
|
-
return age >= 18;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// 变异体
|
|
595
|
-
function isAdult(age) {
|
|
596
|
-
return age > 18; // >= 变为 >
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
// 如果测试没有覆盖边界值 18,变异体存活
|
|
600
|
-
// 说明测试不够充分
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
## 测试最佳实践
|
|
604
|
-
|
|
605
|
-
### AAA 模式
|
|
606
|
-
```python
|
|
607
|
-
def test_user_registration():
|
|
608
|
-
# Arrange - 准备
|
|
609
|
-
user_data = {'username': 'alice', 'email': 'alice@example.com'}
|
|
610
|
-
|
|
611
|
-
# Act - 执行
|
|
612
|
-
result = register_user(user_data)
|
|
613
|
-
|
|
614
|
-
# Assert - 断言
|
|
615
|
-
assert result.success is True
|
|
616
|
-
assert result.user.username == 'alice'
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
### 测试命名
|
|
620
|
-
```javascript
|
|
621
|
-
// ❌ 不好
|
|
622
|
-
test('test1', () => {});
|
|
623
|
-
|
|
624
|
-
// ✅ 好
|
|
625
|
-
test('should return 404 when user not found', () => {});
|
|
626
|
-
test('should create order with valid items', () => {});
|
|
627
|
-
test('should throw error when stock insufficient', () => {});
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
### 单一职责
|
|
631
|
-
```python
|
|
632
|
-
# ❌ 测试多个功能
|
|
633
|
-
def test_user_operations():
|
|
634
|
-
user = create_user()
|
|
635
|
-
update_user(user)
|
|
636
|
-
delete_user(user)
|
|
637
|
-
|
|
638
|
-
# ✅ 拆分测试
|
|
639
|
-
def test_create_user():
|
|
640
|
-
user = create_user()
|
|
641
|
-
assert user.id is not None
|
|
642
|
-
|
|
643
|
-
def test_update_user():
|
|
644
|
-
user = create_user()
|
|
645
|
-
updated = update_user(user, {'name': 'Bob'})
|
|
646
|
-
assert updated.name == 'Bob'
|
|
647
|
-
|
|
648
|
-
def test_delete_user():
|
|
649
|
-
user = create_user()
|
|
650
|
-
delete_user(user)
|
|
651
|
-
assert find_user(user.id) is None
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
## 工具清单
|
|
655
|
-
|
|
656
|
-
| 工具 | 用途 | 语言 |
|
|
657
|
-
|------|------|------|
|
|
658
|
-
| Jest | 单元/集成测试 | JavaScript |
|
|
659
|
-
| Pytest | 单元/集成测试 | Python |
|
|
660
|
-
| JUnit 5 | 单元/集成测试 | Java |
|
|
661
|
-
| Pact | 契约测试 | 多语言 |
|
|
662
|
-
| Testcontainers | 集成测试 | Java/Go/Python |
|
|
663
|
-
| Stryker | 变异测试 | JavaScript |
|
|
664
|
-
| Pitest | 变异测试 | Java |
|
|
665
|
-
| Faker | 测试数据生成 | 多语言 |
|
|
666
|
-
|
|
667
|
-
---
|