[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
---
|
||||
id: wiki-2026-0508-green-check-mark-syndrome
|
||||
title: Green Check Mark Syndrome
|
||||
category: 10_Wiki/Topics
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [Green Check Syndrome, CI Theater, Test Theater]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
verification_status: applied
|
||||
tags: [anti-pattern, ci-cd, testing, observability]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: TypeScript/Python
|
||||
framework: GitHub-Actions/pytest
|
||||
---
|
||||
|
||||
# Green Check Mark Syndrome
|
||||
|
||||
## 매 한 줄
|
||||
> **"매 green CI ≠ correct system."** 매 anti-pattern: 매 passing tests 의 confidence inflation, 매 actual coverage / assertion strength / production behavior 의 disconnect. 매 2026 prevalent — 매 LLM-generated tests, 매 mock-heavy suites, 매 flaky-retry hides 매 real bugs.
|
||||
|
||||
## 매 핵심
|
||||
|
||||
### 매 Symptoms
|
||||
- 매 100% green builds, 매 still production incidents 의 frequent.
|
||||
- 매 tests assert truisms (`expect(1).toBe(1)`).
|
||||
- 매 mocks return canned data — 매 integration paths 의 untested.
|
||||
- 매 retries hide flakiness — 매 race conditions 의 ignored.
|
||||
- 매 coverage % high, 매 mutation score low.
|
||||
|
||||
### 매 Root causes
|
||||
- **Goodhart's law**: 매 green check 의 metric → metric 의 target → 매 gamed.
|
||||
- **Mock theater**: 매 unit isolation 의 over-mocked, 매 real failure modes 의 missed.
|
||||
- **AI-generated tests**: 매 LLM이 매 implementation을 매 mirror — 매 same bug 의 test에도 present.
|
||||
- **Flaky-retry culture**: 매 "retry until green" 의 normalized.
|
||||
|
||||
### 매 Detection
|
||||
- Mutation testing — 매 assertion strength measurement.
|
||||
- Property-based testing — 매 input space coverage.
|
||||
- Production observability — 매 errors in prod that tests 의 missed.
|
||||
- Test-impact analysis — 매 untouched code paths surface.
|
||||
|
||||
### 매 응용
|
||||
1. CI quality dashboard — 매 mutation score + flake rate.
|
||||
2. Test review checklist — 매 each test 의 specific failure mode가 매 catch?
|
||||
3. Chaos engineering — 매 production-like failures inject.
|
||||
|
||||
## 💻 패턴
|
||||
|
||||
### Mutation testing (Stryker)
|
||||
```javascript
|
||||
// stryker.conf.json
|
||||
{
|
||||
"mutate": ["src/**/*.ts"],
|
||||
"testRunner": "vitest",
|
||||
"thresholds": { "high": 80, "low": 60, "break": 50 }
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
npx stryker run
|
||||
# 매 surviving mutants 의 매 weak tests indicate
|
||||
```
|
||||
|
||||
### Property-based test
|
||||
```typescript
|
||||
import { fc } from 'fast-check';
|
||||
|
||||
test('reverse twice = identity', () => {
|
||||
fc.assert(
|
||||
fc.property(fc.array(fc.integer()), (arr) => {
|
||||
expect(reverse(reverse(arr))).toEqual(arr);
|
||||
})
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### Flake-detector (no silent retry)
|
||||
```yaml
|
||||
# .github/workflows/test.yml
|
||||
- name: Test
|
||||
run: npm test
|
||||
# NO retry — 매 flake 의 immediately surface
|
||||
- name: Flake report
|
||||
if: failure()
|
||||
run: |
|
||||
echo "::warning::Test failed — investigate, do not retry blindly"
|
||||
```
|
||||
|
||||
### Assertion-strength linter
|
||||
```python
|
||||
# detect weak assertions
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
WEAK_PATTERNS = {'assertTrue(True)', 'assertEqual(1, 1)', 'assert True'}
|
||||
|
||||
for file in Path('tests').rglob('*.py'):
|
||||
tree = ast.parse(file.read_text())
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Call) and ast.unparse(node) in WEAK_PATTERNS:
|
||||
print(f"WEAK: {file}:{node.lineno}")
|
||||
```
|
||||
|
||||
### Production-trace replay
|
||||
```python
|
||||
# 매 prod traces → test fixtures
|
||||
import json
|
||||
from opentelemetry.trace import get_tracer
|
||||
|
||||
def replay_prod_trace(trace_id: str):
|
||||
trace = fetch_trace(trace_id) # from observability backend
|
||||
inputs = extract_inputs(trace)
|
||||
result = run_system(inputs)
|
||||
expected = trace.outputs
|
||||
assert result == expected, f"Drift from prod: {trace_id}"
|
||||
```
|
||||
|
||||
### Real integration (no mocks)
|
||||
```typescript
|
||||
// testcontainers — 매 real DB
|
||||
import { GenericContainer } from 'testcontainers';
|
||||
|
||||
let pg: StartedTestContainer;
|
||||
beforeAll(async () => {
|
||||
pg = await new GenericContainer('postgres:17')
|
||||
.withExposedPorts(5432)
|
||||
.withEnvironment({ POSTGRES_PASSWORD: 'test' })
|
||||
.start();
|
||||
});
|
||||
|
||||
test('real query', async () => {
|
||||
const client = connect(pg.getMappedPort(5432));
|
||||
await client.query('CREATE TABLE u (id int)');
|
||||
// 매 real SQL behavior tested
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| New test suite | Property-based + integration |
|
||||
| Existing green-but-fragile suite | Mutation testing audit |
|
||||
| Flaky test | Investigate root cause, never blind-retry |
|
||||
| Mock-heavy suite | Add testcontainers for real I/O |
|
||||
| Coverage-driven culture | Switch metric to mutation score |
|
||||
|
||||
**기본값**: 매 green CI 의 trust 의 X — 매 mutation score + 매 prod observability + 매 chaos drills 의 combined signal.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Software-Anti-Patterns]] · [[CI-CD]]
|
||||
- 변형: [[Test-Theater]] · [[Coverage-Gaming]] · [[Mock-Theater]]
|
||||
- 응용: [[Mutation-Testing]] · [[Property-Based-Testing]] · [[Chaos-Engineering]]
|
||||
- Adjacent: [[Goodharts-Law]] · [[Observability]] · [[Flaky-Tests]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: test review (assertion strength critique), mutation report triage, prod-trace replay generation.
|
||||
**언제 X**: 매 LLM이 매 test generation 단독 — 매 same blind spots reproduce.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **Coverage as quality**: 매 100% line coverage, 매 0% mutation kill rate.
|
||||
- **Auto-retry on fail**: 매 race condition 의 hide → prod incident.
|
||||
- **Mock everything**: 매 unit "passes", 매 integration broken.
|
||||
- **LLM-only test suite**: 매 implementation mirror — 매 bug parity.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (Hillel Wayne "Test theater", Google Testing Blog "Just Say No to More End-to-End Tests").
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — CI theater anti-pattern, mutation testing, real integration |
|
||||
Reference in New Issue
Block a user