Sinh test Legacy
Module 9.3: Sinh test Legacy
Phần tiêu đề “Module 9.3: Sinh test Legacy”Thời gian học: ~35 phút
Yêu cầu trước: Module 9.2 (Refactoring từng phần)
Kết quả: Sau module này, bạn sẽ biết dùng Claude Code generate characterization test cho legacy code, hiểu test gì skip gì, và có workflow add test trước khi refactor.
1. WHY — Tại Sao Cần Hiểu
Phần tiêu đề “1. WHY — Tại Sao Cần Hiểu”Muốn refactor function 500 dòng. Không có test. “Refactor careful rồi manual test.” Famous last words.
Refactor xong, seems work, deploy. Sáng hôm sau: production incident. Code path hiếm dùng bị break. Customer complain. Scramble fix.
Test là safety net cho refactoring. No test = no safety net = high risk. Claude Code generate test cho legacy code nhanh hơn bạn viết — cho bạn safety net trước khi bắt đầu change.
2. CONCEPT — Ý Tưởng Cốt Lõi
Phần tiêu đề “2. CONCEPT — Ý Tưởng Cốt Lõi”Characterization Test vs Unit Test
Phần tiêu đề “Characterization Test vs Unit Test”| Loại | Câu Hỏi Nó Trả Lời |
|---|---|
| Unit Test | Code làm cái nó SHOULD làm? |
| Characterization Test | Code HIỆN TẠI làm gì? |
Legacy code “correct” vì đang production. User depend on current behavior. Characterization test capture behavior đó — dù có vẻ sai. Goal là detect change, không phải verify correctness.
Test Generation Workflow
Phần tiêu đề “Test Generation Workflow”graph LR A[Read Code] --> B[Identify Behavior] B --> C[Generate Test] C --> D[Run & Fix Test] D --> E[Verify Coverage] E --> F[Safe to Refactor]Key insight: Khi test fail, fix TEST, không fix code. Code “đúng” — nó đang chạy production.
Test Gì Trong Legacy Code
Phần tiêu đề “Test Gì Trong Legacy Code”| Priority | What | Why |
|---|---|---|
| High | Public interface / API | Code khác dùng |
| High | Happy path flow | Usage phổ biến nhất |
| Medium | Edge case visible | Handling explicit |
| Medium | Error handling | Failure mode quan trọng |
| Low | Internal helper | Sẽ change khi refactor |
Coverage Goal
Phần tiêu đề “Coverage Goal”Không cần 100%. Aim cho “đủ để refactor safe”:
- 70-80% coverage của function cụ thể là đủ
- Focus code sắp change
- Test behavior, không test line
3. DEMO — Từng Bước
Phần tiêu đề “3. DEMO — Từng Bước”Scenario: Legacy function calculateDiscount() — no test, sắp refactor.
Step 1: Read và Identify Behavior
Phần tiêu đề “Step 1: Read và Identify Behavior”Bạn: Read calculateDiscount() trong pricingService.js.List all behavior/path qua function này.
Claude:1. Return 0 nếu user not logged in2. Return 10% cho regular user3. Return 15% cho premium user4. Return 30% cho VIP user5. Cap discount ở 50% bất kể user type6. Throw error nếu amount negativeStep 2: Generate Characterization Test
Phần tiêu đề “Step 2: Generate Characterization Test”Bạn: Generate Jest test cho calculateDiscount() capture TẤT CẢ behavior đó.Đây là characterization test — capture nó LÀM GÌ, không phải nó SHOULD làm gì.
Claude: [Generate test file với 6 test case]Step 3: Run và Verify
Phần tiêu đề “Step 3: Run và Verify”$ npm test pricingService.test.jsOutput:
PASS pricingService.test.js calculateDiscount ✓ return 0 cho non-logged-in user ✓ return 10% cho regular user ✓ return 15% cho premium user ✓ return 30% cho VIP user ✓ cap ở 50% max discount ✓ throw on negative amount
6 tests passedStep 4: Handle Test Failure
Phần tiêu đề “Step 4: Handle Test Failure”Giả sử test fail — Claude assume sai behavior:
FAIL: expected 20% cho premium, got 15%Bạn: Test fail. CODE đúng — nó đang production.Actual discount cho premium là 15%, không phải 20%.Fix test để match actual behavior.
Claude: [Fix test assertion từ 20% thành 15%]Step 5: Check Coverage
Phần tiêu đề “Step 5: Check Coverage”$ npm run test:coverage -- --collectCoverageFrom="**/pricingService.js"Output:
pricingService.js | 85% coverageĐủ để refactor safe.
Step 6: Giờ Safe Để Refactor
Phần tiêu đề “Step 6: Giờ Safe Để Refactor”Bạn: Có test rồi. Refactor calculateDiscount() sang strategy patternthay vì if-else chain.
Bất kỳ refactoring nào change behavior sẽ bị test catch.4. PRACTICE — Tự Thực Hành
Phần tiêu đề “4. PRACTICE — Tự Thực Hành”Bài 1: Test What Exists
Phần tiêu đề “Bài 1: Test What Exists”Goal: Generate characterization test cho existing code.
Instructions:
- Tìm function không có test trong project nào đó
- Ask Claude list all behavior/path
- Generate test cho mỗi behavior
- Run test — all should pass (nếu không, fix test)
- Check coverage
💡 Hint
"Read [function]. What are all possible execution path?Generate test case cho mỗi path."Bài 2: Golden Master
Phần tiêu đề “Bài 2: Golden Master”Goal: Capture complex output làm regression baseline.
Instructions:
- Pick function với complex output (formatting, calculation)
- Run với 10 different input, capture output
- Ask Claude generate test assert exact output đó
- Giờ có regression detection
Bài 3: Test Before Refactor
Phần tiêu đề “Bài 3: Test Before Refactor”Goal: Practice full workflow.
Instructions:
- Pick function muốn refactor
- Generate characterization test
- Achieve 70%+ coverage
- Do small refactor
- Run test — catch được gì không?
✅ Solution
Workflow:
"List all behavior trong [function].""Generate test cho mỗi behavior."- Run test, fix nếu fail (fix TEST, không fix code)
- Check coverage, add thêm test nếu cần
- Refactor với confidence
5. CHEAT SHEET
Phần tiêu đề “5. CHEAT SHEET”Test Generation Workflow
Phần tiêu đề “Test Generation Workflow”- Read code, list behavior
- Generate test cho mỗi behavior
- Run test (expect all pass)
- Nếu fail: fix TEST, không fix code
- Check coverage
- Giờ safe to refactor
Key Prompt
Phần tiêu đề “Key Prompt”"List all behavior/path trong [function].""Generate characterization test capturing current behavior.""Test fail nhưng CODE đúng. Fix test.""Edge case nào code này handle?"Coverage Guideline
Phần tiêu đề “Coverage Guideline”| Goal | Target |
|---|---|
| Minimum | 70% của function-to-refactor |
| Good | 80% với edge case |
| Overkill | 100% (không đáng effort) |
Characterization vs Unit Test
Phần tiêu đề “Characterization vs Unit Test”| Characterization | Unit |
|---|---|
| Nó LÀM GÌ? | Nó SHOULD làm gì? |
| Fix test khi fail | Fix code khi fail |
| Trước refactoring | Khi development |
6. PITFALLS — Lỗi Thường Gặp
Phần tiêu đề “6. PITFALLS — Lỗi Thường Gặp”| ❌ Sai Lầm | ✅ Đúng Cách |
|---|---|
| Fix code khi test fail | Fix TEST. Code “đúng” vì đang production. |
| Aim 100% coverage | 70-80% của code-to-refactor là đủ. |
| Test internal helper | Focus public interface. Helper sẽ change. |
| Verify “correct” behavior | Verify CURRENT behavior, dù có vẻ bug. |
| Generate test không run | ALWAYS run. Claude có thể misunderstand. |
| Skip test “I’ll be careful” | Test là safety net. Always add trước refactor. |
| Complex mocking cho legacy | Start với integration-level test. Mock ít hơn. |
7. REAL CASE — Câu Chuyện Thực Tế
Phần tiêu đề “7. REAL CASE — Câu Chuyện Thực Tế”Scenario: Fintech Việt Nam, legacy loan calculation module. 2,000 dòng, zero test, 8 năm tuổi. Business muốn thêm loan type mới. Team sợ touch.
Cách cũ: “Careful thôi” → Thêm loan type → Break edge case calculation → ₫500M miscalculation phát hiện sau 2 tuần → Painful fix và customer complaint.
Cách mới với Claude:
- Claude analyze code, identify 15 calculation path khác nhau
- Generate 45 characterization test trong 3 giờ
- Test reveal 3 undocumented behavior (feature không ai nhớ)
- Achieve 78% coverage core calculation
- Thêm loan type, test catch 2 regression trong khi dev
- Zero production issue
Investment: 3 giờ generate test Saved: Tuần debug, potential ₫ triệu miscalculation
Quote: “Test không để prove correctness. Để prove không break cái đang work 8 năm.”
Tiếp theo: Module 9.4: Phân tích Tech Debt →