Published on

Day 11 - Testing với Claude Code

Authors

1. Mục tiêu bài học

Sau khoảng 2 tiếng, học viên có thể:

  • Dùng Claude Code để khảo sát test setup hiện có trong taskflow-ai trước khi thêm test mới.
  • Phân biệt rõ unit test, integration test, e2e test và biết test nào thuộc tầng nào trong test pyramid.
  • Yêu cầu Claude sinh test cho backend bằng Vitest hoặc Jest nhưng vẫn review assertion như reviewer chịu trách nhiệm production.
  • Viết test backend có giá trị: meaningful assertion, negative path, deterministic setup, test data isolation, không phụ thuộc thứ tự chạy.
  • Viết Playwright e2e cho flow tạo task trên UI, dùng locator ổn định và web-first assertion thay vì sleep thủ công.
  • Hiểu vì sao coverage chỉ là tín hiệu phụ: coverage cao không đồng nghĩa behavior quan trọng đã được kiểm chứng.
  • Thiết kế workflow Claude Code cho testing: explore -> test plan -> implement tests -> run -> diagnose -> review.

2. Bối cảnh thực tế

Sau Day 08, Day 09 và Day 10, taskflow-ai bắt đầu có đủ backend CRUD, database model và frontend task UI. Đây là lúc dễ rơi vào hai cực đoan:

  • Không có test, chỉ chạy app thủ công rồi tin rằng mọi thứ ổn.
  • Có nhiều test do AI sinh ra nhưng assertion yếu, chủ yếu kiểm tra "không crash", snapshot rộng, hoặc mock quá sâu khiến test pass dù behavior sai.

Claude Code rất hữu ích khi cần tạo test nhanh vì nó có thể đọc route, service, schema, component và luồng UI. Nhưng test là nơi developer không được giao quyền quyết định hoàn toàn cho AI. Lý do: một test sai assertion có thể tạo cảm giác an toàn giả. Test tệ còn nguy hiểm hơn không có test vì team bắt đầu tin vào pipeline.

Trong project thật, lỗi testing thường không nằm ở cú pháp Vitest/Jest/Playwright mà nằm ở chất lượng câu hỏi:

  • Test đang bảo vệ behavior nào?
  • Assertion có bắt được bug thật không?
  • Test có deterministic không, hay phụ thuộc thời gian, thứ tự chạy, network, seed data cũ?
  • Test data có bị leak giữa test cases không?
  • Negative path có được test không?
  • E2E test có kiểm tra đúng outcome người dùng nhìn thấy không?
  • Coverage tăng vì test có ý nghĩa, hay chỉ vì chạm dòng code?

Claude Code nên được dùng như một test assistant: đọc code, đề xuất test matrix, tạo boilerplate, chạy test, phân tích failure. Developer vẫn là người duyệt test intent, assertion và trade-off.

Không nên dùng Claude Code để tự động "tăng coverage lên 90%" mà không có test plan. Mục tiêu coverage kiểu đó thường đẩy AI tạo test nông, mock implementation detail, hoặc snapshot toàn bộ response/UI. Hãy bắt đầu từ risk: code nào quan trọng nhất, bug nào đắt nhất, contract nào cần khóa lại.

3. Kiến thức nền

Test pyramid là cách phân bổ test theo chi phí và độ tin cậy:

TầngMục tiêuVí dụ trong taskflow-aiTốc độRủi ro nếu lạm dụng
UnitKiểm tra business rule nhỏ, không phụ thuộc I/O thậtcreateTask reject title rỗng, trim title, validate status transitionRất nhanhMock quá sâu, test implementation detail
IntegrationKiểm tra nhiều lớp cùng chạy với boundary thật hơnPOST /tasks qua Fastify/Nest route, validation, service, repository test DBTrung bìnhSetup database phức tạp, test chậm nếu không isolate data
E2EKiểm tra flow người dùng qua browser/API thậtUser mở task page, nhập title, bấm Create, thấy task mớiChậm hơnFlaky nếu locator yếu, data không sạch, phụ thuộc timing

Pyramid không có nghĩa là "chỉ viết thật nhiều unit test". Nó nghĩa là dùng đúng tầng cho đúng rủi ro. Với taskflow-ai, flow tạo task có giá trị ở cả ba tầng:

  • Unit test: service không cho tạo task với title chỉ có khoảng trắng.
  • Integration test: API POST /tasks trả 201, response body đúng contract, database có task mới.
  • E2E test: user tạo task từ UI và thấy task xuất hiện trong list.

Vitest và Jest cho backend

Vitest phù hợp với Node.js + TypeScript hiện đại, đặc biệt nếu repo đã dùng Vite hoặc ESM. Vitest hỗ trợ API quen thuộc như describe, it, expect, mocking qua vi.fn, chạy một lần bằng vitest run, và coverage bằng vitest run --coverage.

Jest vẫn phổ biến trong backend Node.js, đặc biệt trong NestJS hoặc repo đã có setup Jest. Jest có CLI như jest, jest --watch, jest --coverage, jest --runInBand, hỗ trợ async test qua async/await, .resolves, .rejects, và mock function.

Không chọn framework test theo sở thích của Claude. Hãy đọc package.json, config và test hiện có. Nếu backend đã dùng Jest, đừng để Claude thêm Vitest chỉ vì ví dụ mới hơn. Nếu backend đã dùng Vitest, đừng thêm Jest cho một file test đơn lẻ.

Playwright cho e2e

Playwright Test phù hợp để kiểm tra browser flow vì có locator theo role/text/label, auto-wait và web-first assertion. Với flow tạo task, test nên tương tác như user:

  • Mở trang task.
  • Điền title vào textbox có accessible name rõ.
  • Bấm button Create.
  • Kiểm tra task mới xuất hiện.
  • Kiểm tra request/API hoặc database state nếu project đã có fixture phù hợp.

Tránh page.waitForTimeout(1000) như mặc định. Playwright assertion như await expect(locator).toBeVisible() tự retry trong timeout nên ổn định hơn sleep thủ công.

Meaningful assertion

Assertion có giá trị là assertion sẽ fail khi behavior quan trọng sai. Ví dụ:

Assertion yếuAssertion tốt hơn
expect(response.status).toBeLessThan(500)expect(response.status).toBe(201)
expect(body).toBeDefined()expect(body).toMatchObject({ title: "Ship Day 11", status: "open" })
expect(tasks.length).toBeGreaterThan(0)expect(tasks).toContainEqual(expect.objectContaining({ id: created.id, title }))
Snapshot toàn bộ pageCheck heading, form state, new task row, error message cụ thể

Với Claude Code, prompt phải nói rõ: "không chỉ kiểm tra happy path; phải có negative path và assertion cụ thể về contract".

Coverage là tín hiệu phụ

Coverage trả lời câu hỏi "dòng code nào đã được chạy", không trả lời "behavior nào đã được kiểm chứng". Một test có thể chạy qua 95% code nhưng không assert đúng gì quan trọng. Ngược lại, coverage 70% nhưng khóa được critical path có thể đáng tin hơn coverage 95% bằng snapshot nông.

Coverage hữu ích để tìm vùng mù:

  • File quan trọng không có test.
  • Branch validation/error chưa chạy.
  • Code mới không được chạm tới.

Coverage không nên là KPI duy nhất. Hãy kết hợp coverage với test review checklist: assertion, negative path, deterministic setup, data isolation và failure signal.

4. Step-by-step thực hành

Mục tiêu thực hành: dùng Claude Code để bổ sung test backend bằng Vitest hoặc Jest và Playwright e2e cho flow tạo task trong taskflow-ai, không phá scope của các worker khác.

Bước 1: Kiểm tra working tree và xác định test setup

Chạy trong thư mục gốc taskflow-ai:

git status --short

Lệnh này hiển thị file đang thay đổi ở dạng ngắn. Output kỳ vọng là rỗng hoặc chỉ có file bạn hiểu rõ. Rủi ro: nếu có thay đổi của worker khác, đừng rollback hoặc sửa chồng; hãy ghi lại và tránh chạm file đó.

Tìm package Node liên quan:

find . -maxdepth 3 -name package.json

Lệnh này chạy ở root taskflow-ai để tìm package.json trong monorepo. Output kỳ vọng có thể là ./package.json, ./backend/package.json, ./frontend/package.json. Rủi ro thấp vì read-only; trên Windows PowerShell có thể dùng Get-ChildItem -Recurse -Filter package.json -Depth 3.

Đọc script test thật trước khi chạy:

npm pkg get scripts

Lệnh này chạy trong thư mục có package.json của backend hoặc root package. Nó in ra các script như test, test:unit, test:integration, test:e2e. Output kỳ vọng giúp xác định repo đang dùng Vitest hay Jest. Rủi ro: nếu chạy nhầm folder, output có thể là script frontend hoặc root, không phải backend.

Bước 2: Mở Claude Code ở plan mode để khảo sát test hiện có

Chạy trong thư mục gốc taskflow-ai:

claude --permission-mode plan

Lệnh này mở Claude Code ở mode đọc và lập plan trước khi sửa. Output kỳ vọng là session Claude sẵn sàng nhận prompt. Rủi ro thấp hơn implement mode, nhưng Claude vẫn có thể suy đoán nếu chưa đọc đủ file.

Prompt khám phá:

Bạn đang ở repo taskflow-ai. Hãy khảo sát test setup hiện có trước khi đề xuất test mới.

Ràng buộc:
- Chưa sửa file.
- Chỉ đọc file cần thiết.
- Tìm backend package, test runner đang dùng là Vitest hay Jest, config test, test helpers, database test setup, API route tasks và frontend task flow nếu có.
- Nêu rõ file đã đọc và bằng chứng từ code.
- Không đề xuất thêm dependency hoặc đổi test runner.
- Output gồm: test commands thật, test gaps, rủi ro flaky, và đề xuất test matrix cho flow tạo task.

Kỳ vọng: Claude liệt kê package.json, config như vitest.config.ts hoặc jest.config.*, test helper, route/service task, Playwright config nếu có. Nếu Claude chưa đọc test setup mà đã đề xuất file test, yêu cầu đọc bổ sung.

Bước 3: Chốt test matrix trước khi viết test

Gửi prompt trong cùng session:

Dựa trên code đã đọc, hãy lập test matrix cho flow tạo task.

Yêu cầu:
- Tách rõ unit, integration, e2e.
- Mỗi test case ghi behavior cần bảo vệ, fixture/setup, assertion chính, negative path nếu có.
- Với backend, ưu tiên meaningful assertion về status code, response body, error code/message, database side effect.
- Với e2e Playwright, ưu tiên locator theo role/label/text và web-first assertion.
- Ghi rõ test nào không nên viết vì quá brittle hoặc trùng tầng.
- Chưa sửa file.

Test matrix tốt nên có dạng:

TầngCaseAssertion chính
UnitcreateTask reject title whitespacethrow/return validation error cụ thể
UnitcreateTask normalize titletitle trong result đã trim, status default đúng
IntegrationPOST /tasks success201, body có id, title, status, database có row
IntegrationPOST /tasks invalid title400, error code đúng, database không tạo row
E2EUser tạo task từ UIform submit thành công, task mới hiển thị trong list

Nếu Claude đề xuất snapshot toàn response hoặc toàn page, yêu cầu thay bằng assertion cụ thể.

Bước 4: Implement backend test bằng Vitest hoặc Jest

Sau khi duyệt test matrix, mở session implement có boundary hẹp. Chạy trong root taskflow-ai:

claude --permission-mode default --allowedTools "Read" "Edit" "Bash(git status *)" "Bash(git diff *)" "Bash(npm run test*)" "Bash(npm test *)"

Lệnh này cho Claude đọc, edit và chạy một số command test/Git read-only. Mỗi rule trong --allowedTools được truyền riêng để bám cú pháp CLI hiện tại; nếu version Claude Code của bạn khác, kiểm tra lại bằng claude --help hoặc official CLI reference trước khi dùng. Output kỳ vọng là session sẵn sàng. Rủi ro: allowlist vẫn có thể chạy script test tốn thời gian hoặc chạm database test; không thêm npm install, migration, docker destructive command nếu chưa duyệt.

Prompt implement backend test:

Implement backend tests cho flow tạo task theo test matrix đã approve.

Ràng buộc:
- Dùng test runner hiện có trong backend: Vitest hoặc Jest. Không thêm test runner mới.
- Chỉ sửa/tạo file test và test helper đã nằm trong plan.
- Không sửa production code trừ khi test phát hiện bug rõ và phải hỏi tôi trước.
- Không mock quá sâu qua service/repository nếu integration test cần kiểm tra route contract.
- Unit test phải có negative path cho title rỗng/whitespace.
- Integration test phải assert status code, response body, error shape và database side effect hoặc repository call phù hợp với setup hiện có.
- Test data phải isolated: unique title/id per test, cleanup bằng helper hiện có hoặc transaction/reset test DB.
- Không dùng snapshot rộng.
- Sau khi edit, tóm tắt file changed và command test nên chạy.

Ví dụ nếu backend dùng Vitest, lệnh chạy một lần thường là:

npm run test -- --run

Lệnh này chạy trong folder backend có package.json. Nó thường buộc Vitest chạy một lần thay vì watch mode. Output kỳ vọng là test pass và exit code 0. Rủi ro: nếu script test không trỏ tới Vitest hoặc không nhận --run, command có thể fail; đọc script thật trước.

Ví dụ nếu backend dùng Jest:

npm test -- --runInBand

Lệnh này chạy trong folder backend có package.json để chạy Jest tuần tự, hữu ích khi debug test có database hoặc shared resource. Output kỳ vọng là test pass và exit code 0. Rủi ro: chạy tuần tự chậm hơn; nếu test phụ thuộc thứ tự thì pass tuần tự chưa chắc pass khi parallel trong CI.

Chạy coverage như tín hiệu phụ, không phải mục tiêu chính. Với Vitest:

npm run test -- --run --coverage

Lệnh này chạy trong folder backend nếu script hỗ trợ Vitest và coverage provider đã được cài/config. Output kỳ vọng là bảng coverage gồm statements/branches/functions/lines. Rủi ro: coverage có thể yêu cầu dependency provider hoặc config; đừng để Claude thêm dependency chỉ để đạt số đẹp nếu team chưa duyệt.

Với Jest:

npm test -- --coverage

Lệnh này chạy trong folder backend để tạo coverage report bằng Jest. Output kỳ vọng là bảng coverage và exit code theo ngưỡng hiện có. Rủi ro: coverage chạy chậm hơn và có thể fail vì threshold cũ, không nhất thiết vì test mới sai.

Bước 5: Implement Playwright e2e cho flow tạo task

Trước tiên kiểm tra Playwright đã có trong project chưa. Chạy trong root taskflow-ai hoặc frontend package nếu repo tách package:

npm pkg get devDependencies

Lệnh này đọc devDependencies để xem có @playwright/test không. Output kỳ vọng có package này nếu e2e đã setup. Rủi ro: nếu chạy nhầm package, bạn có thể tưởng Playwright chưa có dù nó nằm ở root hoặc package khác.

Nếu project đã có Playwright config, chạy danh sách test:

npx playwright test --list

Lệnh này chạy ở package có playwright.config.* để liệt kê test mà không mở browser chạy thật. Output kỳ vọng là danh sách spec/test names. Rủi ro thấp; nếu config có global setup nặng, vẫn có thể tốn thời gian.

Nếu chưa có Playwright và team đã duyệt thêm dependency, command setup phổ biến là:

npm init playwright@latest

Lệnh này chạy ở root package hoặc package frontend theo kiến trúc repo. Nó tạo/cập nhật Playwright config, e2e folder và cài dependency theo prompt tương tác. Output kỳ vọng là wizard setup hoàn tất. Rủi ro: command có thể sửa package.json, lockfile và tạo example tests; chỉ chạy sau khi plan được approve, không chạy trong repo nhiều worker nếu chưa thống nhất.

Prompt implement e2e:

Implement Playwright e2e test cho flow tạo task.

Ràng buộc:
- Dùng Playwright setup hiện có; nếu chưa có, chỉ đề xuất setup, chưa chạy install.
- Test phải dùng locator ổn định: getByRole, getByLabel, getByText có name cụ thể. Không dùng selector CSS brittle nếu UI có accessible name.
- Không dùng page.waitForTimeout.
- Setup test data deterministic: unique title theo timestamp/test id, cleanup bằng API/helper nếu có.
- Nếu app cần backend/frontend server, dùng webServer trong playwright config hiện có hoặc ghi rõ command cần chạy.
- Assertion chính: user submit task thành công và task mới hiển thị trong list; nếu API error, UI hiển thị error message rõ.
- Không snapshot toàn page.
- Chỉ sửa/tạo file e2e trong plan.

Chạy e2e tập trung:

npx playwright test e2e/create-task.spec.ts --project=chromium

Lệnh này chạy ở package có playwright.config.* để chạy riêng spec tạo task trên Chromium. Output kỳ vọng là test pass, kèm trace/screenshot nếu config bật khi fail. Rủi ro: cần app server, test database và port đúng; nếu chạy trên data dev không sạch, test có thể flaky.

Debug e2e bằng headed mode:

npx playwright test e2e/create-task.spec.ts --project=chromium --headed --debug

Lệnh này chạy ở package có Playwright config, mở browser thật và dừng cho debug. Output kỳ vọng là Playwright Inspector hoặc browser headed. Rủi ro: không phù hợp CI; có thể treo session nếu không tương tác.

Xem trace khi test fail:

npx playwright show-trace trace.zip

Lệnh này chạy ở nơi có file trace.zip do Playwright tạo. Output kỳ vọng là Trace Viewer mở lên để xem action, DOM snapshot, network. Rủi ro: trace có thể chứa dữ liệu form hoặc token test; không upload public nếu có thông tin nhạy cảm.

Bước 6: Review test do Claude sinh

Chạy trong root taskflow-ai:

git diff --stat

Lệnh này cho biết phạm vi thay đổi. Output kỳ vọng chỉ gồm file test, test helper, config Playwright nếu đã approve. Rủi ro: nếu production code hoặc lockfile bị sửa ngoài plan, dừng lại và review trước.

Xem diff chi tiết:

git diff

Lệnh này hiển thị patch chi tiết. Output kỳ vọng: test có assertion cụ thể, negative path, setup/teardown rõ, không snapshot rộng, không sleep. Rủi ro: diff test dài dễ bỏ sót assertion yếu; review theo từng test case.

Prompt review:

Review diff test hiện tại như senior test reviewer, không sửa file.

Tập trung:
- Test case có bảo vệ behavior quan trọng không.
- Assertion có meaningful không hay chỉ kiểm tra không crash.
- Có negative path cho validation/error không.
- Setup có deterministic không, có phụ thuộc thời gian/network/data cũ không.
- Test data có isolated không.
- Có mock quá sâu làm mất giá trị integration test không.
- Playwright locator có ổn định và accessible không.
- Có snapshot rộng hoặc waitForTimeout không.
- Coverage tăng có đi kèm chất lượng assertion không.

Kết luận theo format: Blocker, Should fix, Nice to have, Test gaps.

Bước 7: Diagnose failure trước khi sửa

Nếu test fail, không cho Claude sửa ngay. Gửi failure ngắn:

Test fail như sau. Hãy phân tích nguyên nhân trước, chưa sửa file.

Yêu cầu:
- Phân loại: bug production code, test sai contract, setup thiếu, data isolation lỗi, timing/flaky, hoặc environment.
- Chỉ ra assertion nào fail và behavior mong đợi là gì.
- Đề xuất patch nhỏ nhất.
- Nếu cần sửa production code, hỏi lại tôi trước.

Nếu failure do data cũ, ưu tiên sửa fixture/setup thay vì nới assertion. Nếu failure do UI locator yếu, ưu tiên cải thiện accessible name hoặc locator theo role, không chuyển sang CSS selector dài nếu có lựa chọn tốt hơn.

5. Prompt mẫu nên dùng

Prompt khám phá codebase

Hãy khảo sát test setup trong taskflow-ai.

Ràng buộc:
- Chưa sửa file.
- Tìm backend package, frontend package, test runner, Playwright config, test helper, database test setup và command test thật.
- Nêu file đã đọc và bằng chứng.
- Không đề xuất đổi Vitest sang Jest hoặc ngược lại.
- Output gồm test matrix sơ bộ cho flow tạo task và rủi ro flaky.

Prompt lập test plan

Lập test plan cho flow tạo task.

Yêu cầu:
- Chia unit/integration/e2e.
- Mỗi test ghi behavior, setup, assertion chính, negative path.
- Chỉ chọn test có giá trị regression rõ.
- Ghi test nào không nên viết vì trùng tầng hoặc brittle.
- Không implement.

Prompt implement backend test

Implement backend tests theo test plan đã approve.

Giới hạn:
- Dùng test runner hiện có: Vitest hoặc Jest.
- Chỉ sửa file test/test helper trong plan.
- Unit test business rule; integration test API contract.
- Assertion phải kiểm tra status, body, error shape và side effect.
- Có negative path cho invalid title.
- Test data isolated, không phụ thuộc thứ tự chạy.
- Không snapshot rộng, không mock quá sâu.
- Không sửa production code nếu chưa hỏi.

Prompt implement Playwright e2e

Implement Playwright e2e cho create task flow.

Giới hạn:
- Dùng Playwright config hiện có.
- Locator theo role/label/text; không waitForTimeout.
- Unique test data và cleanup nếu có helper.
- Assertion: task mới xuất hiện, form/error state đúng.
- Nếu cần server command, dùng script hiện có và ghi rõ.
- Chỉ sửa file e2e trong plan.

Prompt review

Review diff test hiện tại, không sửa file.

Kiểm tra:
- Assertion có bắt đúng bug không.
- Negative path đủ chưa.
- Setup deterministic chưa.
- Data isolation có sạch không.
- Test có phụ thuộc implementation detail không.
- Playwright locator có accessible và ổn định không.
- Coverage có tăng nhờ test meaningful hay chỉ do gọi hàm.

Trả lời theo Blocker, Should fix, Nice to have, Test gaps.

Prompt test/diagnose

Đây là output test fail. Hãy diagnose, chưa sửa file.

Phân loại lỗi:
- production bug
- test sai contract
- setup thiếu
- data isolation
- flaky/timing
- environment

Sau đó đề xuất patch nhỏ nhất và command verify lại.

6. Trade-offs

Viết nhiều unit test nhanh và rẻ, nhưng không chứng minh route wiring, validation HTTP boundary, serialization hay database side effect đúng. Với task creation, unit test chỉ nên khóa rule cốt lõi như trim/reject title, status default, permission rule nếu có.

Integration test đắt hơn vì cần app instance, database hoặc repository fake có chủ đích. Đổi lại, nó bắt được contract drift: status code sai, error shape sai, route không đăng ký, validation không chạy, database không ghi. Với backend API, integration test thường có ROI cao hơn unit test mock quá sâu.

E2E test chậm và dễ flaky hơn, nhưng là test duy nhất bảo vệ workflow người dùng thực sự: UI form, API call, loading state, success state, list refresh. Không nên e2e mọi edge case. Hãy chọn một happy path critical và một vài negative path UI quan trọng, còn phần validation chi tiết để unit/integration test.

Snapshot có thể hữu ích cho output nhỏ, ổn định và có chủ đích. Snapshot rộng cho response lớn hoặc toàn DOM thường tạo noise: reviewer không đọc kỹ, AI update snapshot cho pass, bug lọt qua. Với Day 11, mặc định tránh snapshot rộng.

Coverage threshold giúp ngăn vùng code mới không test, nhưng dễ bị game. Team nên dùng coverage như guardrail mềm: xem branch quan trọng chưa được cover, không dùng nó thay thế review test intent.

Cho Claude tự chạy test giúp nhanh hơn, nhưng command test có thể tốn thời gian, chạm test database hoặc tạo artifact. Dùng allowlist hẹp, đọc script trước và không để Claude chạy install/migration khi chưa duyệt.

7. Best practices

  • Bắt đầu bằng test matrix, không bắt đầu bằng "write tests for this file".
  • Review assertion trước khi review style. Test đẹp nhưng assertion yếu vẫn là test tệ.
  • Mỗi bug fix quan trọng nên có regression test fail trước hoặc ít nhất test case mô tả rõ bug.
  • Unit test không nên mock chính function đang cần kiểm tra. Mock ở boundary, không mock behavior trung tâm.
  • Integration test nên kiểm tra contract public: status code, response body, error code/message, database side effect hoặc event side effect.
  • Playwright test ưu tiên getByRole, getByLabel, getByText với accessible name cụ thể. Locator càng giống cách user hiểu UI càng bền.
  • Không dùng waitForTimeout để chữa flaky. Dùng web-first assertion, chờ network/state đúng hoặc cải thiện UI signal.
  • Test data phải unique và cleanup được. Với task title, dùng prefix như e2e-create-task-${testInfo.workerIndex}-${Date.now()} hoặc helper tương đương.
  • Không chạy test trên production credential, production database hoặc shared staging data nếu test có ghi dữ liệu.
  • Không đưa secret, token, connection string thật hoặc dữ liệu khách hàng vào prompt, trace, screenshot hay test fixture. Nếu cần debug trace, coi trace như artifact nhạy cảm.
  • Không để Claude tự update snapshot hoặc coverage threshold chỉ để pipeline xanh.
  • Khi test fail, diagnose nguyên nhân trước. Không nới assertion cho pass nếu behavior chưa đúng.
  • Giữ test maintainable: tên test mô tả behavior, helper dùng lại cho setup lặp lại, fixture ngắn và rõ, không assert vào implementation detail dễ đổi.
  • Nếu test buộc phải sửa production code, yêu cầu Claude nêu rõ bug, contract bị vi phạm, patch nhỏ nhất và test regression đi kèm trước khi edit.
  • Với repo nhiều worker, không rollback toàn repo. Chỉ rollback file test bạn tạo/sửa và đã xác nhận không thuộc worker khác.

8. Performance / cost / context

Testing task dễ tốn context vì Claude cần đọc production code, test config, fixtures, helpers, scripts và đôi khi UI. Giảm chi phí bằng cách buộc Claude đọc theo lớp:

  1. package.json và test config.
  2. Test helper/setup hiện có.
  3. Code production liên quan tới create task.
  4. Test tương tự gần nhất.
  5. Playwright config và page/component liên quan.

Không yêu cầu Claude đọc toàn repo để "hiểu test". Với backend, chỉ cần route/service/repository/schema/test helper liên quan. Với e2e, chỉ cần page flow, component labels, Playwright config và seed/cleanup helper.

Runtime cũng cần kiểm soát:

  • Unit test nên chạy nhanh, không cần database thật.
  • Integration test nên dùng test database/transaction/reset helper, không sleep.
  • E2E test nên ít nhưng chất lượng, chạy song song được nếu data isolated.
  • Coverage chỉ chạy khi cần review vùng mù hoặc trong CI, không nhất thiết mỗi vòng local.
  • Khi dùng Claude, đưa failure output ngắn và đúng phần fail; không paste toàn bộ log nếu log dài hàng nghìn dòng.

Context summary hữu ích sau khi đã chốt test matrix:

Context summary cho session tiếp theo:
- Backend test runner: Vitest/Jest theo package thật.
- Test command: ...
- Files approved to edit: ...
- Create task contract: ...
- Test cases approved: ...
- Không được: thêm dependency, sửa production code nếu chưa hỏi, snapshot rộng, waitForTimeout.

9. Checklist cuối bài

  • Tôi đã kiểm tra git status --short trước khi cho Claude sửa test.
  • Tôi đã yêu cầu Claude đọc test runner/config/script thật trước khi đề xuất test.
  • Tôi biết backend đang dùng Vitest hay Jest và không để Claude thêm runner mới.
  • Tôi có test matrix cho unit, integration và e2e của flow tạo task.
  • Unit test có negative path cho title rỗng/whitespace hoặc business rule tương đương.
  • Integration test assert status code, response body, error shape và side effect quan trọng.
  • Playwright e2e dùng locator ổn định và web-first assertion.
  • Không có snapshot rộng hoặc waitForTimeout không có lý do.
  • Test data isolated, deterministic và có cleanup/reset phù hợp.
  • Coverage được xem như tín hiệu phụ, không thay thế review assertion.
  • Tôi đã review git diff --statgit diff.
  • Tôi đã diagnose failure trước khi sửa hoặc nới assertion.

10. Bài tập

Bài cơ bản: mở taskflow-ai bằng claude --permission-mode plan, yêu cầu Claude khảo sát test setup và lập test matrix cho flow tạo task. Không cho sửa file. Kết quả cần có: test runner thật, command test thật, file đã đọc, test gaps và test matrix unit/integration/e2e.

Bài nâng cao: cho Claude implement backend tests cho create task bằng test runner hiện có. Phải có ít nhất một unit test cho business rule và một integration test cho API contract. Review assertion và ghi lại ít nhất 3 điểm bạn yêu cầu Claude sửa.

Bài áp dụng e2e: thêm Playwright spec cho flow tạo task từ UI. Test phải dùng locator theo role/label/text, không dùng sleep, có unique test data và assertion rằng task mới hiển thị sau submit. Chạy spec tập trung và xem trace nếu fail.

Bài áp dụng vào project cá nhân: chọn một flow quan trọng trong repo cá nhân. Viết test matrix trước khi nhờ Claude code. Sau khi Claude sinh test, phân loại từng test: meaningful, weak, duplicate, brittle. Xóa hoặc sửa test yếu thay vì giữ để tăng coverage.


Tài liệu

Tóm tắt kiến thức

Day 11 tập trung vào testing với Claude Code, nhưng trọng tâm không phải là "AI viết test thật nhanh". Trọng tâm là dùng Claude để tăng tốc phần cơ học của testing trong khi developer vẫn kiểm soát test intent, assertion và rủi ro flaky.

Các nguyên tắc chính:

  • Test pyramid giúp phân tầng test theo chi phí và mục tiêu: unit nhanh và hẹp, integration khóa contract giữa nhiều lớp, e2e kiểm tra flow người dùng.
  • Unit test nên bảo vệ business rule nhỏ: validate input, normalize dữ liệu, status transition, permission rule.
  • Integration test nên bảo vệ API contract: status code, response body, error shape, database side effect, route wiring.
  • E2E test nên bảo vệ flow quan trọng từ góc nhìn user: tạo task từ UI, thấy task mới xuất hiện, lỗi validation hiển thị đúng.
  • Claude Code có thể sinh boilerplate test tốt, nhưng developer phải review assertion. Assertion yếu tạo cảm giác an toàn giả.
  • Coverage chỉ cho biết code đã được chạy, không chứng minh behavior đã được kiểm chứng. Coverage là tín hiệu phụ để tìm vùng mù.
  • Test chất lượng cần có meaningful assertion, negative path, deterministic setup, test data isolation, và tránh snapshot rộng.

Với taskflow-ai, flow tạo task nên được bảo vệ ở ba tầng:

TầngBehavior nên testAssertion chính
UnitTitle sau trim không được rỗngtrả validation error cụ thể hoặc throw domain error
UnitTask mới có default status đúngresult có status: "open" hoặc value theo contract
IntegrationPOST /tasks tạo task thành công201, body có id/title/status, database/repository có task
IntegrationPOST /tasks reject title invalid400, error shape đúng, không tạo dữ liệu
E2EUser tạo task từ UItask mới hiển thị trong list, form state đúng

Claude Code nên đi theo workflow:

Explore test setup
  -> Test matrix
  -> Plan file-by-file
  -> Implement tests
  -> Run focused tests
  -> Diagnose failure
  -> Review assertions

Sơ đồ tư duy hoặc luồng xử lý

Yêu cầu: testing create task flow
  |
  v
Kiểm tra git status
  |
  v
Claude Code plan mode
  |
  +-- đọc package.json
  +-- đọc Vitest/Jest config
  +-- đọc test helper/setup
  +-- đọc task route/service/schema
  +-- đọc Playwright config/UI flow
  |
  v
Test matrix
  |
  +-- Unit: business rule
  +-- Integration: API contract
  +-- E2E: user flow
  |
  v
Developer review matrix
  |
  +-- assertion có meaningful?
  +-- negative path đủ chưa?
  +-- setup deterministic chưa?
  +-- data isolation rõ chưa?
  |
  v
Implement test bằng Claude
  |
  v
Run focused tests
  |
  +-- Pass -> review diff + coverage như tín hiệu phụ
  |
  +-- Fail -> diagnose trước khi sửa
            |
            +-- production bug
            +-- test sai contract
            +-- setup thiếu
            +-- data isolation
            +-- flaky/timing
            +-- environment

Luồng quyết định chọn tầng test:

Behavior là pure business rule?
  -> Unit test

Behavior cần route/validation/error/database cùng chạy?
  -> Integration test

Behavior chỉ có giá trị khi user thao tác qua browser?
  -> E2E test

Behavior đã được test ở tầng thấp và e2e chỉ lặp lại chi tiết?
  -> Không viết e2e, giữ test thấp hơn

Bảng so sánh

Tiêu chíUnit testIntegration testE2E test
Mục tiêuKhóa rule nhỏKhóa contract giữa nhiều lớpKhóa workflow user
Ví dụcreateTask reject title whitespacePOST /tasks trả 400 và không ghi DBUser nhập title, submit, thấy task
Tốc độNhanh nhấtTrung bìnhChậm nhất
Flaky riskThấp nếu không dùng time/random không kiểm soátTrung bình nếu test DB/setup kémCao hơn nếu locator/data/timing kém
Claude hỗ trợ tốt ở đâuSinh case matrix, boilerplate, mocksĐọc route/test helper, sinh request assertionsSinh spec, locator, debug trace
Developer phải review gìMock boundary, assertion, edge casesContract, database side effect, cleanupLocator, wait strategy, test data, real user outcome
Chủ đềNên làmKhông nên làm
AssertionKiểm tra status/body/error/side effect cụ thểtoBeDefined, not.toThrow cho mọi thứ
Negative pathTest invalid title, empty body, not found, permission nếu cóChỉ test happy path
Data isolationUnique data per test, cleanup/reset rõDùng seed data cũ hoặc title cố định dễ đụng
MockingMock ở boundary ngoài hệ thống cần testMock chính service đang muốn kiểm chứng
SnapshotSnapshot nhỏ, ổn định, có chủ đíchSnapshot toàn response/page rộng
Playwright waitWeb-first assertion, locator ổn địnhwaitForTimeout để che flaky
CoverageDùng để tìm vùng mùDùng làm mục tiêu duy nhất
SecurityDùng test DB/credential riêng, giữ trace/screenshot như artifact nhạy cảmChạy test ghi dữ liệu bằng production credential hoặc paste secret vào prompt
MaintainabilityTên test theo behavior, helper setup rõ, assertion bám public contractTest implementation detail, fixture quá lớn, helper che mất intent
CommandChạy ở đâuDùng để làm gìOutput kỳ vọngRủi ro
git status --shortRoot taskflow-aiKiểm tra working tree trước khi sửaRỗng hoặc file đã hiểu rõCó thể thấy thay đổi của worker khác; không rollback
npm pkg get scriptsFolder có package.json backend/rootXem script test thậtJSON scripts có test, test:unit, test:e2e nếu cóChạy nhầm package sẽ đọc sai script
claude --permission-mode planRoot taskflow-aiMở Claude ở mode khảo sát/lập planSession sẵn sàng nhận promptClaude vẫn có thể suy đoán nếu đọc thiếu file
npm run test -- --runBackend package dùng VitestChạy Vitest một lầnTest pass, exit code 0Script có thể không nhận --run nếu không phải Vitest
npm test -- --runInBandBackend package dùng JestChạy Jest tuần tự để debugTest pass, exit code 0Chậm hơn, không đại diện parallel CI
npm run test -- --run --coverageBackend package dùng VitestXem coverage VitestBảng coverageCó thể thiếu coverage provider/config
npm test -- --coverageBackend package dùng JestXem coverage JestBảng coverageDễ bị dùng sai như KPI duy nhất
npx playwright test --listPackage có Playwright configLiệt kê testDanh sách spec/testCó thể tốn thời gian nếu global setup nặng
npx playwright test e2e/create-task.spec.ts --project=chromiumPackage có Playwright configChạy riêng create task e2eSpec pass, trace nếu failCần app server/test DB/port đúng
npx playwright show-trace trace.zipNơi có file traceDebug e2e failureTrace Viewer mở đượcTrace có thể chứa dữ liệu nhạy cảm

Lỗi thường gặp

  1. Để Claude viết test trước khi đọc test setup
    Kết quả thường là thêm runner mới, import sai helper, hoặc tạo test không chạy trong CI. Cách sửa: bắt Claude đọc package.json, config và test tương tự trước.

  2. Assertion quá yếu
    Test kiểu expect(response).toBeDefined() pass dù status/body sai. Cách sửa: yêu cầu assertion về status code, response body, error code/message và side effect.

  3. Chỉ test happy path
    Flow tạo task pass với title hợp lệ nhưng không test title rỗng, whitespace, body thiếu field. Cách sửa: test matrix bắt buộc có negative path.

  4. Mock quá sâu
    Integration test mock route, service và repository đến mức không còn kiểm tra integration. Cách sửa: mock external boundary, không mock lớp trung tâm của behavior đang cần kiểm chứng.

  5. Test data không isolated
    E2E dùng title cố định Test task, test pass/fail tùy data cũ. Cách sửa: unique title, cleanup bằng API/helper, hoặc reset test DB.

  6. Playwright locator brittle
    Selector như .container > div:nth-child(3) button dễ vỡ khi UI đổi layout. Cách sửa: dùng getByRole, getByLabel, getByText với accessible name rõ.

  7. Dùng sleep để chữa flaky
    waitForTimeout làm test chậm mà không giải quyết race condition. Cách sửa: dùng web-first assertion hoặc đợi state cụ thể.

  8. Snapshot rộng
    Snapshot toàn DOM/response khiến reviewer update snapshot cho pass mà không hiểu thay đổi. Cách sửa: assertion cụ thể vào field/element quan trọng.

  9. Coverage cao nhưng behavior thấp
    AI có thể gọi function để tăng dòng chạy nhưng không assert gì đáng kể. Cách sửa: review test intent trước khi nhìn coverage.

  10. Sửa production code vì test fail mà chưa diagnose
    Có thể test sai contract hoặc setup thiếu. Cách sửa: phân loại failure trước, chỉ sửa production code khi xác định bug thật.

Cách debug

Khi không biết backend dùng Vitest hay Jest, chạy:

npm pkg get scripts

Chạy trong folder backend có package.json. Lệnh này in script test thật. Output kỳ vọng cho thấy vitest, jest, test:unit, test:integration hoặc script tương đương. Rủi ro: nếu backend nằm trong subfolder mà bạn chạy ở root, output có thể không phản ánh package backend.

Nếu muốn tìm config test:

find . -maxdepth 4 \( -name "vitest.config.*" -o -name "jest.config.*" -o -name "playwright.config.*" \)

Chạy ở root taskflow-ai. Lệnh này tìm các file config test thường gặp. Output kỳ vọng là path tới config hiện có. Rủi ro thấp vì read-only; trên Windows PowerShell dùng Get-ChildItem -Recurse -Include vitest.config.*,jest.config.*,playwright.config.*.

Khi backend test fail, chạy tập trung vào test file hoặc pattern nếu script hỗ trợ:

npm run test -- --run tasks

Chạy trong backend package dùng Vitest. Lệnh này cố chạy test liên quan tasks một lần. Output kỳ vọng là danh sách test task pass/fail. Rủi ro: filter syntax phụ thuộc runner/script; nếu không chắc, đọc docs hoặc script trước.

Với Jest:

npm test -- tasks --runInBand

Chạy trong backend package dùng Jest để lọc test path/name liên quan tasks và chạy tuần tự. Output kỳ vọng là failure tập trung hơn. Rủi ro: pattern có thể match quá rộng hoặc quá hẹp; không kết luận coverage tổng thể từ lệnh lọc.

Khi E2E fail, chạy headed/debug:

npx playwright test e2e/create-task.spec.ts --project=chromium --headed --debug

Chạy ở package có Playwright config. Lệnh này mở browser để quan sát flow. Output kỳ vọng là browser/inspector cho thấy bước fail. Rủi ro: debug mode không phù hợp CI và có thể treo nếu bạn không đóng session.

Khi cần xem trace:

npx playwright show-trace trace.zip

Chạy ở nơi có trace.zip từ failure. Output kỳ vọng là Trace Viewer. Rủi ro: trace có thể chứa dữ liệu nhập vào form hoặc token test; không chia sẻ public.

Prompt debug nên dùng với Claude:

Đây là failure output rút gọn. Hãy diagnose, chưa sửa file.

Phân loại lỗi:
- production bug
- test sai contract
- setup thiếu
- data isolation
- flaky/timing
- environment

Chỉ ra assertion fail, nguyên nhân có khả năng nhất, và patch nhỏ nhất để verify lại.

Khi nghi ngờ test do Claude viết không có giá trị:

Review các test mới theo tiêu chí test quality.

Với từng test, hãy cho biết:
- Behavior được bảo vệ là gì.
- Bug nào test này sẽ bắt được.
- Assertion nào là assertion chính.
- Test có deterministic không.
- Có trùng tầng hoặc brittle không.

Không sửa file.

Bài tập

Bài 1 — Cơ bản

Mục tiêu: dùng Claude Code ở plan mode để khảo sát test setup của taskflow-ai và lập test matrix cho flow tạo task, chưa sửa file.

Yêu cầu:

  1. Mở terminal tại thư mục gốc taskflow-ai.

  2. Kiểm tra working tree:

git status --short

Lệnh này chạy ở root repo để xem file đang thay đổi. Output kỳ vọng là rỗng hoặc chỉ có file bạn hiểu rõ. Rủi ro: nếu có thay đổi của worker khác, không sửa hoặc rollback các file đó.

  1. Tìm package Node:
find . -maxdepth 3 -name package.json

Lệnh này chạy ở root repo để tìm package backend/frontend/root. Output kỳ vọng là một hoặc nhiều path package.json. Rủi ro thấp vì read-only; trên Windows PowerShell có thể dùng Get-ChildItem -Recurse -Filter package.json -Depth 3.

  1. Đọc scripts trong package nghi là backend:
npm pkg get scripts

Lệnh này chạy trong folder có package.json backend hoặc root package. Output kỳ vọng là JSON script có test hoặc script liên quan. Rủi ro: chạy nhầm package sẽ dẫn tới test command sai.

  1. Mở Claude Code ở plan mode:
claude --permission-mode plan

Lệnh này chạy ở root taskflow-ai để mở session khảo sát/lập plan. Output kỳ vọng là Claude sẵn sàng nhận prompt. Rủi ro: plan vẫn có thể sai nếu Claude đọc thiếu file.

  1. Gửi prompt:
Bạn đang ở repo taskflow-ai. Hãy khảo sát test setup cho flow tạo task.

Ràng buộc:
- Chưa sửa file.
- Tìm test runner backend đang dùng: Vitest hay Jest.
- Tìm test commands thật, test config, test helpers, database test setup.
- Tìm Playwright config hoặc e2e setup nếu có.
- Đọc route/service/schema liên quan tới create task và UI flow tạo task nếu có.
- Nêu rõ file đã đọc và bằng chứng từ code.
- Không đề xuất đổi test runner hoặc thêm dependency.
- Output gồm test gaps, rủi ro flaky, và test matrix unit/integration/e2e.

Kết quả cần nộp:

  • Danh sách file Claude đã đọc.
  • Test runner backend đang dùng và command test thật.
  • Playwright đã có hay chưa.
  • Test matrix cho create task flow.
  • 3 rủi ro nếu cho Claude viết test ngay mà chưa review matrix.

Bài 2 — Thực tế

Mục tiêu: dùng Claude Code implement backend tests cho flow tạo task bằng Vitest hoặc Jest hiện có.

Yêu cầu:

  1. Từ test matrix ở Bài 1, chọn tối thiểu 3 test backend:
  • Unit: reject title rỗng hoặc chỉ có khoảng trắng.
  • Unit: normalize/default field khi tạo task.
  • Integration: POST /tasks success hoặc validation error theo contract hiện có.
  1. Yêu cầu Claude lập plan file-by-file:
Lập plan implement backend tests cho create task flow.

Ràng buộc:
- Dùng test runner hiện có trong backend: Vitest hoặc Jest.
- Không thêm dependency.
- Chỉ sửa/tạo file test và test helper nếu plan nêu rõ.
- Không sửa production code trong bước này.
- Mỗi test case phải ghi setup, assertion chính, negative path nếu có.
- Test data phải isolated và deterministic.
- Chờ tôi approve trước khi edit.
  1. Sau khi approve, mở session implement:
claude --permission-mode default --allowedTools "Read" "Edit" "Bash(git status *)" "Bash(git diff *)" "Bash(npm run test*)" "Bash(npm test *)"

Lệnh này chạy ở root taskflow-ai để Claude được đọc, edit và chạy một số command test/Git read-only. Mỗi permission rule được truyền riêng để tránh nhập nhằng với cú pháp --allowedTools; nếu CLI local khác, kiểm tra claude --help trước. Output kỳ vọng là session sẵn sàng. Rủi ro: script test có thể chạm test database hoặc chạy lâu; không mở rộng allowlist sang install/migration nếu chưa duyệt.

  1. Gửi prompt implement:
Implement backend tests theo plan đã approve.

Ràng buộc bắt buộc:
- Không thêm test runner mới.
- Không sửa production code nếu test chưa chứng minh bug; nếu cần sửa, dừng và hỏi.
- Assertion phải meaningful: status code, response body, error shape, side effect.
- Có negative path cho invalid title.
- Test data unique hoặc cleanup bằng helper hiện có.
- Không snapshot rộng.
- Không phụ thuộc thứ tự chạy test.
- Sau khi edit, tóm tắt diff và command verify.
  1. Review phạm vi patch:
git diff --stat

Lệnh này chạy ở root repo để xem file/dòng thay đổi. Output kỳ vọng chỉ gồm file test/test helper trong plan. Rủi ro: nếu production code hoặc lockfile bị sửa ngoài plan, dừng lại và review.

  1. Chạy test backend theo runner thật.

Nếu backend dùng Vitest:

npm run test -- --run

Lệnh này chạy trong folder backend có package.json để chạy Vitest một lần. Output kỳ vọng là test pass và exit code 0. Rủi ro: nếu script không phải Vitest hoặc không nhận --run, command fail; đọc script trước.

Nếu backend dùng Jest:

npm test -- --runInBand

Lệnh này chạy trong folder backend có package.json để chạy Jest tuần tự. Output kỳ vọng là test pass và exit code 0. Rủi ro: chạy tuần tự chậm hơn và có thể không phát hiện vấn đề parallel isolation.

  1. Gửi prompt review:
Review diff backend tests hiện tại, không sửa file.

Tập trung:
- Assertion có bắt đúng behavior quan trọng không.
- Negative path đủ chưa.
- Setup có deterministic không.
- Test data có isolated không.
- Có mock quá sâu hoặc test implementation detail không.
- Có snapshot rộng không.

Kết luận theo Blocker, Should fix, Nice to have, Test gaps.

Kết quả cần nộp:

  • File test đã tạo/sửa.
  • Test command đã chạy và output chính.
  • Ít nhất 3 nhận xét review assertion.
  • Quyết định giữ, sửa, hoặc bỏ test nào.

Bài 3 — Nâng cao

Mục tiêu: thêm Playwright e2e cho flow tạo task trên UI của taskflow-ai.

Yêu cầu:

  1. Kiểm tra Playwright setup:
npm pkg get devDependencies

Lệnh này chạy trong root package hoặc package frontend/e2e theo repo. Nó đọc dependency để xem có @playwright/test không. Output kỳ vọng có package này nếu e2e đã setup. Rủi ro: chạy nhầm package sẽ đọc sai dependency.

  1. Nếu có Playwright config, liệt kê test:
npx playwright test --list

Lệnh này chạy ở package có playwright.config.*. Output kỳ vọng là danh sách test hiện có. Rủi ro: một số config có global setup tốn thời gian.

  1. Nếu chưa có Playwright, không tự cài ngay. Yêu cầu Claude đề xuất setup:
Repo chưa thấy Playwright setup rõ ràng. Hãy đề xuất plan thêm Playwright e2e cho taskflow-ai.

Ràng buộc:
- Chưa chạy install.
- Nêu package nào sẽ bị sửa, command nào cần chạy, file config/spec nào sẽ tạo.
- Nêu rủi ro với lockfile, CI, app server, test database.
- Chờ tôi approve.

Nếu team approve setup mới, command phổ biến là:

npm init playwright@latest

Lệnh này chạy ở root package hoặc frontend/e2e package theo plan. Output kỳ vọng là wizard tạo Playwright config và example. Rủi ro: sửa package.json, lockfile và tạo file mẫu; chỉ chạy sau khi approve, đặc biệt trong repo có nhiều worker.

  1. Yêu cầu Claude lập plan e2e:
Lập plan Playwright e2e cho create task flow.

Ràng buộc:
- Dùng Playwright config hiện có nếu có.
- Chỉ tạo/sửa spec e2e trong plan.
- Test data phải unique.
- Nếu có API/helper cleanup, dùng helper đó; nếu không, ghi rõ cleanup thủ công hoặc reset test DB.
- Locator phải dùng getByRole/getByLabel/getByText khi có accessible name.
- Không dùng waitForTimeout.
- Assertion chính: task mới xuất hiện trong list sau submit.
- Chờ tôi approve trước khi edit.
  1. Cho Claude implement:
Implement Playwright create task e2e theo plan đã approve.

Ràng buộc:
- Không snapshot toàn page.
- Không selector CSS brittle nếu có role/label/text.
- Không dùng waitForTimeout.
- Không sửa UI chỉ để test pass nếu chưa hỏi.
- Nếu phát hiện thiếu accessible label, dừng và đề xuất patch nhỏ thay vì tự đổi rộng.
- Sau khi edit, tóm tắt file changed và command verify.
  1. Chạy spec tập trung:
npx playwright test e2e/create-task.spec.ts --project=chromium

Lệnh này chạy ở package có Playwright config. Output kỳ vọng là spec pass, hoặc failure có trace/screenshot nếu config bật. Rủi ro: cần frontend/backend server, test database và port đúng.

  1. Nếu fail, debug:
npx playwright test e2e/create-task.spec.ts --project=chromium --headed --debug

Lệnh này chạy ở package Playwright để mở browser và inspector. Output kỳ vọng là bạn thấy bước fail. Rủi ro: không dùng trong CI, có thể treo session nếu không tương tác.

  1. Nếu có trace:
npx playwright show-trace trace.zip

Lệnh này chạy ở nơi có trace.zip. Output kỳ vọng là Trace Viewer. Rủi ro: trace có thể chứa dữ liệu nhập trong test; không chia sẻ public nếu có thông tin nhạy cảm.

Kết quả cần nộp:

  • Spec e2e đã tạo.
  • Locator chính đã dùng và lý do ổn định.
  • Cách tạo/cleanup test data.
  • Output test hoặc phân tích failure.

Bài 4 — Review & Reflection

Mục tiêu: đánh giá chất lượng test do Claude Code tạo và biến kết quả thành rule làm việc cho team.

Yêu cầu:

  1. Chạy coverage như tín hiệu phụ.

Nếu backend dùng Vitest:

npm run test -- --run --coverage

Lệnh này chạy trong backend package để tạo coverage report nếu config hỗ trợ. Output kỳ vọng là bảng coverage. Rủi ro: coverage có thể cần provider/config; không thêm dependency chỉ để làm đẹp số nếu team chưa duyệt.

Nếu backend dùng Jest:

npm test -- --coverage

Lệnh này chạy trong backend package để tạo coverage report. Output kỳ vọng là bảng coverage. Rủi ro: coverage chậm hơn và có thể fail vì threshold, không nhất thiết do test mới sai.

  1. Gửi prompt review chất lượng:
Review toàn bộ test mới của Day 11, không sửa file.

Với từng test, hãy trả lời:
- Behavior nào được bảo vệ.
- Bug nào test này sẽ bắt được.
- Assertion chính có meaningful không.
- Có negative path không.
- Setup có deterministic và isolated không.
- Test có duplicate tầng khác hoặc brittle không.
- Nếu coverage tăng, tăng đó có giá trị hay chỉ là chạy qua code.

Kết luận: test nên giữ, test nên sửa, test nên xóa.
  1. Trả lời reflection:
  • Test nào có giá trị nhất trong backend? Vì sao?
  • Test nào dễ flaky nhất? Bạn đã giảm rủi ro như thế nào?
  • Claude Code đề xuất assertion yếu nào? Bạn sửa ra sao?
  • Coverage sau khi thêm test nói gì và không nói gì?
  • Bạn sẽ thêm rule gì vào CLAUDE.md cho các task test sau?
  1. Viết 8-10 rule đề xuất cho team. Prompt gợi ý:
Dựa trên Day 11, hãy đề xuất rule testing cho CLAUDE.md của taskflow-ai.

Yêu cầu:
- Rule phải cụ thể cho Vitest/Jest backend và Playwright e2e.
- Có rule về meaningful assertion, negative path, deterministic setup, data isolation, không snapshot rộng, không waitForTimeout.
- Có rule về coverage là tín hiệu phụ.
- Có rule về việc Claude không được sửa production code khi test fail nếu chưa hỏi.

Kết quả cần nộp:

  • Coverage summary và nhận xét ngắn.
  • Danh sách test giữ/sửa/xóa.
  • Reflection 10-15 dòng.
  • Rule testing đề xuất cho CLAUDE.md.

Tiêu chí hoàn thành

  • Đã dùng claude --permission-mode plan để khảo sát test setup trước khi sửa.
  • Xác định đúng backend dùng Vitest hay Jest, không thêm runner mới.
  • Có test matrix rõ cho unit, integration và e2e của flow tạo task.
  • Backend test có meaningful assertion, không chỉ kiểm tra không crash.
  • Có negative path cho input invalid, tối thiểu title rỗng/whitespace hoặc case tương đương theo contract.
  • Integration test assert status code, response body, error shape và side effect quan trọng.
  • Playwright e2e dùng locator ổn định theo role/label/text khi có thể.
  • Không dùng snapshot rộng hoặc waitForTimeout để che flaky.
  • Test data deterministic và isolated; có cleanup/reset hoặc unique data strategy.
  • Đã chạy test command phù hợp và ghi lại output chính.
  • Đã review diff và phân loại test gaps.
  • Coverage được dùng để tìm vùng mù, không dùng làm thước đo chất lượng duy nhất.
  • Không đưa secret, token, production data hoặc trace/screenshot chứa dữ liệu nhạy cảm vào prompt hay artifact public.
  • Test mới dễ bảo trì: tên test mô tả behavior, helper/fixture rõ ràng, không khóa implementation detail không thuộc contract.

Gợi ý nếu bí

Nếu Claude không tìm được test runner:

Hãy đọc package.json liên quan, lockfile, vitest.config.*, jest.config.*, và các file *.test.* hoặc *.spec.* gần nhất.
Chưa sửa file. Kết luận repo đang dùng runner nào và command nào chạy test.

Nếu Claude sinh assertion yếu:

Assertion này quá yếu. Hãy viết lại test để assertion fail khi contract sai.
Với API, kiểm tra status code, response body, error shape và side effect.
Với UI, kiểm tra element người dùng nhìn thấy và trạng thái form/error cụ thể.

Nếu integration test cần database nhưng setup chưa rõ:

Chưa rõ database test setup. Hãy đọc test helper/setup hiện có và đề xuất 2 phương án isolation:
1. transaction/reset helper hiện có
2. repository fake hoặc in-memory store nếu project đã dùng pattern đó
Chưa sửa file.

Nếu Playwright test flaky:

Test e2e đang flaky. Hãy phân tích nguyên nhân, chưa sửa file.
Kiểm tra locator, wait strategy, data isolation, server readiness, network race.
Không đề xuất waitForTimeout trừ khi có lý do bất khả kháng.

Nếu coverage cao nhưng bạn không tin test:

Coverage tăng nhưng tôi muốn đánh giá test quality.
Hãy map từng test tới behavior và bug mà test bắt được.
Test nào không có assertion meaningful thì đề xuất sửa hoặc xóa.
Không sửa file.

Đáp án tham khảo hoặc expected result

Kết quả tốt cho Bài 1:

  • Claude đọc đúng package.json, test config, test helper, route/service task và Playwright config nếu có.
  • Test runner backend được xác định rõ: Vitest hoặc Jest.
  • Test matrix có đủ unit, integration và e2e, không trộn mục tiêu.
  • Có rủi ro flaky và data isolation được nêu trước khi implement.

Kết quả tốt cho Bài 2:

  • Có unit test reject title rỗng/whitespace.
  • Có unit test cho normalize/default field hoặc business rule tương đương.
  • Có integration test POST /tasks success hoặc invalid input.
  • Assertion kiểm tra status/body/error/side effect cụ thể.
  • Test data unique hoặc cleanup rõ.
  • git diff --stat chỉ gồm file test/helper trong plan.

Ví dụ assertion backend chấp nhận được:

// File ví dụ: backend/src/tasks/task.service.test.ts
// Mục đích: khóa business rule title sau trim không được rỗng.
// Cách test: chạy unit test backend bằng Vitest/Jest script hiện có.
// Edge case: title chỉ gồm khoảng trắng phải bị reject.
await expect(service.createTask({ title: "   " })).rejects.toMatchObject({
  code: "TASK_TITLE_REQUIRED",
});

Đoạn code trên chỉ là ví dụ minh họa. Trong repo thật, error shape phải bám convention hiện có của taskflow-ai.

Kết quả tốt cho Bài 3:

  • Playwright spec tạo task bằng UI thật hoặc flow dev/test tương đương.
  • Locator dùng getByRole, getByLabel hoặc getByText với name cụ thể.
  • Không có waitForTimeout.
  • Title task unique theo test run.
  • Assertion chính kiểm tra task mới xuất hiện trong list.

Ví dụ Playwright assertion chấp nhận được:

// File ví dụ: e2e/create-task.spec.ts
// Mục đích: khóa flow người dùng tạo task từ UI.
// Cách test: chạy npx playwright test e2e/create-task.spec.ts --project=chromium.
// Edge case: locator dựa trên role/label để tránh phụ thuộc layout CSS.
await page.getByRole("textbox", { name: /task title/i }).fill(title);
await page.getByRole("button", { name: /create task/i }).click();
await expect(page.getByRole("listitem", { name: new RegExp(title) })).toBeVisible();

Kết quả tốt cho Bài 4:

  • Coverage summary được ghi lại nhưng không dùng để tuyên bố test tốt.
  • Reflection chỉ ra ít nhất một test meaningful và một test cần sửa/xóa.
  • Rule đề xuất cho CLAUDE.md có thể dùng ngay, ví dụ:
- Khi viết test backend, dùng test runner hiện có; không thêm Vitest/Jest mới nếu repo đã chọn runner.
- Mỗi API test phải assert status code, response body, error shape và side effect quan trọng.
- Mỗi feature test phải có ít nhất một negative path có giá trị.
- Test data phải deterministic và isolated; không phụ thuộc seed data cũ hoặc thứ tự test.
- Không dùng snapshot rộng cho response/page.
- Playwright test ưu tiên getByRole/getByLabel/getByText; không dùng waitForTimeout để che flaky.
- Coverage là tín hiệu phụ để tìm vùng mù, không thay thế review assertion.
- Khi test fail, Claude phải diagnose trước; không sửa production code nếu chưa được approve.