{"product_id":"test-driven-development-với-claude-code-viết-test-trước-code-sau","title":"Test-Driven Development với Claude Code — Viết test trước, code sau","description":"\n\u003cp\u003eTest-Driven Development (TDD) là phương pháp phát triển phần mềm nơi bạn viết test trước khi viết code. Nghe có vẻ ngược đời, nhưng khi kết hợp với Claude Code, TDD trở thành một workflow cực kỳ hiệu quả: bạn mô tả yêu cầu dưới dạng test cases, Claude Code viết code để pass tất cả tests, và bạn có một safety net hoàn chỉnh cho mọi lần refactoring sau này. Bài viết này hướng dẫn chi tiết cách áp dụng TDD với Claude Code qua các ví dụ thực tế với pytest và Jest.\u003c\/p\u003e\n\n\u003ch2\u003eTDD là gì và tại sao nên dùng với Claude Code?\u003c\/h2\u003e\n\u003cp\u003eTDD tuân theo chu trình Red-Green-Refactor:\u003c\/p\u003e\n\u003col\u003e\n  \u003cli\u003e\n\u003cstrong\u003eRed:\u003c\/strong\u003e Viết test mô tả hành vi mong muốn. Chạy test -- test sẽ fail vì chưa có code.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eGreen:\u003c\/strong\u003e Viết code tối thiểu để pass test. Không cần code đẹp, chỉ cần pass.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eRefactor:\u003c\/strong\u003e Cải thiện code trong khi giữ tất cả tests pass. Đây là lúc bạn làm code sạch và tối ưu.\u003c\/li\u003e\n\u003c\/ol\u003e\n\n\u003cp\u003eKhi kết hợp với Claude Code, mỗi bước trở nên nhanh hơn:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eRed:\u003c\/strong\u003e Bạn viết test (hoặc nhờ Claude Code viết test từ requirements). Test fail xác nhận rằng test đang kiểm tra đúng thứ.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eGreen:\u003c\/strong\u003e Claude Code đọc test, hiểu expected behavior, và sinh code implementation. Vì test là specification rõ ràng nhất, Claude Code có context chính xác để viết code đúng.\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eRefactor:\u003c\/strong\u003e Bạn yêu cầu Claude Code refactor, rồi chạy lại tests để đảm bảo không break gì.\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eTại sao TDD + Claude Code hiệu quả hơn cách truyền thống?\u003c\/h3\u003e\n\u003cp\u003eKhi bạn yêu cầu Claude Code \"viết hàm tính giá sau discount\", kết quả phụ thuộc vào cách Claude hiểu yêu cầu. Nhưng khi bạn cung cấp test cases cụ thể, Claude Code có specification chính xác. Test case chính là dạng tốt nhất của requirement documentation -- không mơ hồ, có thể verify tự động.\u003c\/p\u003e\n\n\u003ch2\u003eWorkflow TDD với Claude Code\u003c\/h2\u003e\n\n\u003ch3\u003eBước 1: Mô tả requirements cho Claude Code, yêu cầu sinh tests\u003c\/h3\u003e\n\u003cp\u003ePrompt trong Claude Code terminal:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eTôi cần xây dựng module quản lý giỏ hàng (shopping cart)\nvới các yêu cầu sau:\n\n1. Thêm sản phẩm vào giỏ (tên, giá, số lượng)\n2. Xóa sản phẩm khỏi giỏ\n3. Cập nhật số lượng sản phẩm\n4. Tính tổng tiền\n5. Áp dụng mã giảm giá (% hoặc số tiền cố định)\n6. Giỏ hàng không được có sản phẩm với số lượng \u0026lt;= 0\n7. Mã giảm giá không được giảm quá 50% tổng đơn\n\nHãy viết test cases bằng pytest cho module này TRƯỚC.\nChưa viết implementation. Tổ chức tests theo từng feature.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eBước 2: Claude Code sinh test file\u003c\/h3\u003e\n\u003cp\u003eClaude Code sẽ tạo file test với cấu trúc rõ ràng:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# test_cart.py\nimport pytest\nfrom cart import ShoppingCart, Product, DiscountCode\n\nclass TestAddProduct:\n    def test_add_single_product(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=1)\n        assert len(cart.items) == 1\n        assert cart.items[0].name == \"Áo thun\"\n        assert cart.items[0].price == 250000\n        assert cart.items[0].quantity == 1\n\n    def test_add_multiple_products(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=1)\n        cart.add_product(\"Quần jeans\", price=450000, quantity=2)\n        assert len(cart.items) == 2\n\n    def test_add_existing_product_increases_quantity(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=1)\n        cart.add_product(\"Áo thun\", price=250000, quantity=2)\n        assert len(cart.items) == 1\n        assert cart.items[0].quantity == 3\n\n    def test_add_product_with_zero_quantity_raises(self):\n        cart = ShoppingCart()\n        with pytest.raises(ValueError, match=\"phải lớn hơn 0\"):\n            cart.add_product(\"Áo thun\", price=250000, quantity=0)\n\n    def test_add_product_with_negative_quantity_raises(self):\n        cart = ShoppingCart()\n        with pytest.raises(ValueError, match=\"phải lớn hơn 0\"):\n            cart.add_product(\"Áo thun\", price=250000, quantity=-1)\n\n\nclass TestRemoveProduct:\n    def test_remove_existing_product(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=1)\n        cart.remove_product(\"Áo thun\")\n        assert len(cart.items) == 0\n\n    def test_remove_nonexistent_product_raises(self):\n        cart = ShoppingCart()\n        with pytest.raises(KeyError):\n            cart.remove_product(\"Sản phẩm không tồn tại\")\n\n\nclass TestUpdateQuantity:\n    def test_update_quantity(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=1)\n        cart.update_quantity(\"Áo thun\", 5)\n        assert cart.items[0].quantity == 5\n\n    def test_update_to_zero_removes_product(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=3)\n        cart.update_quantity(\"Áo thun\", 0)\n        assert len(cart.items) == 0\n\n    def test_update_negative_quantity_raises(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=1)\n        with pytest.raises(ValueError):\n            cart.update_quantity(\"Áo thun\", -1)\n\n\nclass TestCalculateTotal:\n    def test_total_single_product(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=2)\n        assert cart.calculate_total() == 500000\n\n    def test_total_multiple_products(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=2)\n        cart.add_product(\"Quần jeans\", price=450000, quantity=1)\n        assert cart.calculate_total() == 950000\n\n    def test_total_empty_cart(self):\n        cart = ShoppingCart()\n        assert cart.calculate_total() == 0\n\n\nclass TestDiscountCode:\n    def test_percentage_discount(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=2)\n        cart.apply_discount(DiscountCode(\"SALE20\", discount_type=\"percent\", value=20))\n        assert cart.calculate_total() == 400000  # 500000 * 0.8\n\n    def test_fixed_amount_discount(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=2)\n        cart.apply_discount(DiscountCode(\"MINUS100K\", discount_type=\"fixed\", value=100000))\n        assert cart.calculate_total() == 400000  # 500000 - 100000\n\n    def test_discount_cannot_exceed_50_percent(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=1)\n        with pytest.raises(ValueError, match=\"không được giảm quá 50%\"):\n            cart.apply_discount(DiscountCode(\"BIG60\", discount_type=\"percent\", value=60))\n\n    def test_fixed_discount_cannot_exceed_50_percent_of_total(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=250000, quantity=1)\n        with pytest.raises(ValueError, match=\"không được giảm quá 50%\"):\n            cart.apply_discount(DiscountCode(\"BIG\", discount_type=\"fixed\", value=200000))\n\n    def test_total_never_goes_negative(self):\n        cart = ShoppingCart()\n        cart.add_product(\"Áo thun\", price=100000, quantity=1)\n        cart.apply_discount(DiscountCode(\"MINUS50K\", discount_type=\"fixed\", value=50000))\n        assert cart.calculate_total() \u0026gt;= 0\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eBước 3: Chạy tests -- xác nhận tất cả FAIL (Red)\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Trong terminal Claude Code\npytest test_cart.py -v\n\n# Kết quả mong đợi: tất cả tests FAIL vì chưa có file cart.py\n# FAILED test_cart.py::TestAddProduct::test_add_single_product - ModuleNotFoundError\n# ...\n# 15 failed\u003c\/code\u003e\u003c\/pre\u003e\n\u003cp\u003eĐây chính là bước Red. Tests fail vì module cart chưa tồn tại. Điều này quan trọng: nó xác nhận tests đang kiểm tra đúng thứ (nếu test pass mà chưa có code, test có vấn đề).\u003c\/p\u003e\n\n\u003ch3\u003eBước 4: Yêu cầu Claude Code viết implementation (Green)\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eĐọc file test_cart.py và viết implementation trong cart.py\nđể tất cả tests pass. Chỉ viết code tối thiểu cần thiết.\u003c\/code\u003e\u003c\/pre\u003e\n\u003cp\u003eClaude Code sẽ đọc tests, hiểu specification, và sinh code. Sau đó chạy tests lại:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003epytest test_cart.py -v\n\n# Kết quả mong đợi: tất cả tests PASS\n# PASSED test_cart.py::TestAddProduct::test_add_single_product\n# PASSED test_cart.py::TestAddProduct::test_add_multiple_products\n# ...\n# 15 passed\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eBước 5: Refactor với safety net (Refactor)\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eRefactor cart.py để:\n1. Tách Product và DiscountCode thành file models.py\n2. Thêm type hints đầy đủ\n3. Tối ưu performance cho calculate_total (dùng cached property)\n4. Đảm bảo tất cả tests vẫn pass sau refactoring\u003c\/code\u003e\u003c\/pre\u003e\n\u003cp\u003eClaude Code refactor code, rồi chạy tests để xác nhận không break gì. Nếu có test fail, Claude Code biết chính xác chỗ nào bị ảnh hưởng và sửa ngay.\u003c\/p\u003e\n\n\u003ch2\u003eTDD với Jest cho TypeScript\/JavaScript\u003c\/h2\u003e\n\u003cp\u003eQuy trình tương tự áp dụng cho dự án TypeScript\/JavaScript với Jest:\u003c\/p\u003e\n\n\u003ch3\u003ePrompt sinh tests:\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eTôi cần module xử lý đặt lịch hẹn (appointment booking) với yêu cầu:\n\n1. Tạo appointment với ngày, giờ, duration, tên khách\n2. Không cho đặt trùng thời gian (overlap detection)\n3. Chỉ đặt được trong giờ làm việc (8:00-17:00)\n4. Duration tối thiểu 30 phút, tối đa 4 giờ\n5. Hủy appointment phải trước ít nhất 2 giờ\n6. Reschedule giữ nguyên appointment ID\n\nViết test cases bằng Jest + TypeScript. Chưa viết implementation.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eClaude Code sinh test file:\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e\/\/ appointment.test.ts\nimport { AppointmentManager, Appointment } from \".\/appointment\";\n\ndescribe(\"AppointmentManager\", () =\u0026gt; {\n  let manager: AppointmentManager;\n\n  beforeEach(() =\u0026gt; {\n    manager = new AppointmentManager();\n  });\n\n  describe(\"createAppointment\", () =\u0026gt; {\n    it(\"should create appointment with valid data\", () =\u0026gt; {\n      const apt = manager.createAppointment({\n        date: \"2026-04-01\",\n        startTime: \"09:00\",\n        duration: 60,\n        customerName: \"Nguyen Van A\",\n      });\n\n      expect(apt.id).toBeDefined();\n      expect(apt.customerName).toBe(\"Nguyen Van A\");\n      expect(apt.duration).toBe(60);\n    });\n\n    it(\"should reject appointment outside business hours\", () =\u0026gt; {\n      expect(() =\u0026gt;\n        manager.createAppointment({\n          date: \"2026-04-01\",\n          startTime: \"07:00\",\n          duration: 60,\n          customerName: \"Nguyen Van A\",\n        })\n      ).toThrow(\"ngoài giờ làm việc\");\n    });\n\n    it(\"should reject appointment ending after business hours\", () =\u0026gt; {\n      expect(() =\u0026gt;\n        manager.createAppointment({\n          date: \"2026-04-01\",\n          startTime: \"16:30\",\n          duration: 60,\n          customerName: \"Nguyen Van A\",\n        })\n      ).toThrow(\"ngoài giờ làm việc\");\n    });\n\n    it(\"should reject duration less than 30 minutes\", () =\u0026gt; {\n      expect(() =\u0026gt;\n        manager.createAppointment({\n          date: \"2026-04-01\",\n          startTime: \"09:00\",\n          duration: 15,\n          customerName: \"Nguyen Van A\",\n        })\n      ).toThrow(\"Duration\");\n    });\n\n    it(\"should reject duration more than 4 hours\", () =\u0026gt; {\n      expect(() =\u0026gt;\n        manager.createAppointment({\n          date: \"2026-04-01\",\n          startTime: \"09:00\",\n          duration: 300,\n          customerName: \"Nguyen Van A\",\n        })\n      ).toThrow(\"Duration\");\n    });\n  });\n\n  describe(\"overlap detection\", () =\u0026gt; {\n    it(\"should reject overlapping appointments\", () =\u0026gt; {\n      manager.createAppointment({\n        date: \"2026-04-01\",\n        startTime: \"09:00\",\n        duration: 60,\n        customerName: \"Nguyen Van A\",\n      });\n\n      expect(() =\u0026gt;\n        manager.createAppointment({\n          date: \"2026-04-01\",\n          startTime: \"09:30\",\n          duration: 60,\n          customerName: \"Tran Van B\",\n        })\n      ).toThrow(\"trùng lịch\");\n    });\n\n    it(\"should allow back-to-back appointments\", () =\u0026gt; {\n      manager.createAppointment({\n        date: \"2026-04-01\",\n        startTime: \"09:00\",\n        duration: 60,\n        customerName: \"Nguyen Van A\",\n      });\n\n      const apt = manager.createAppointment({\n        date: \"2026-04-01\",\n        startTime: \"10:00\",\n        duration: 60,\n        customerName: \"Tran Van B\",\n      });\n\n      expect(apt.id).toBeDefined();\n    });\n\n    it(\"should allow same time different dates\", () =\u0026gt; {\n      manager.createAppointment({\n        date: \"2026-04-01\",\n        startTime: \"09:00\",\n        duration: 60,\n        customerName: \"Nguyen Van A\",\n      });\n\n      const apt = manager.createAppointment({\n        date: \"2026-04-02\",\n        startTime: \"09:00\",\n        duration: 60,\n        customerName: \"Tran Van B\",\n      });\n\n      expect(apt.id).toBeDefined();\n    });\n  });\n\n  describe(\"cancelAppointment\", () =\u0026gt; {\n    it(\"should cancel appointment at least 2 hours before\", () =\u0026gt; {\n      const apt = manager.createAppointment({\n        date: \"2026-04-10\",\n        startTime: \"14:00\",\n        duration: 60,\n        customerName: \"Nguyen Van A\",\n      });\n\n      \/\/ Giả lập thời điểm hiện tại là 11:00 ngày 10\/04\n      const result = manager.cancelAppointment(\n        apt.id,\n        new Date(\"2026-04-10T11:00:00\")\n      );\n      expect(result.status).toBe(\"cancelled\");\n    });\n\n    it(\"should reject cancellation less than 2 hours before\", () =\u0026gt; {\n      const apt = manager.createAppointment({\n        date: \"2026-04-10\",\n        startTime: \"14:00\",\n        duration: 60,\n        customerName: \"Nguyen Van A\",\n      });\n\n      expect(() =\u0026gt;\n        manager.cancelAppointment(\n          apt.id,\n          new Date(\"2026-04-10T12:30:00\")\n        )\n      ).toThrow(\"trước ít nhất 2 giờ\");\n    });\n  });\n\n  describe(\"rescheduleAppointment\", () =\u0026gt; {\n    it(\"should keep same ID after reschedule\", () =\u0026gt; {\n      const apt = manager.createAppointment({\n        date: \"2026-04-10\",\n        startTime: \"09:00\",\n        duration: 60,\n        customerName: \"Nguyen Van A\",\n      });\n\n      const rescheduled = manager.rescheduleAppointment(apt.id, {\n        date: \"2026-04-11\",\n        startTime: \"10:00\",\n      });\n\n      expect(rescheduled.id).toBe(apt.id);\n      expect(rescheduled.date).toBe(\"2026-04-11\");\n      expect(rescheduled.startTime).toBe(\"10:00\");\n    });\n  });\n});\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eSau khi có tests, yêu cầu Claude Code viết implementation:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eĐọc file appointment.test.ts và viết implementation\ntrong appointment.ts để tất cả tests pass.\nDùng TypeScript strict mode, có type definitions đầy đủ.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eCoverage tracking\u003c\/h2\u003e\n\u003cp\u003eTDD tự nhiên tạo ra test coverage cao, nhưng vẫn cần theo dõi để phát hiện gaps:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Python - pytest-cov\npytest test_cart.py --cov=cart --cov-report=term-missing\n\n# JavaScript - Jest coverage\nnpx jest --coverage\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eYêu cầu Claude Code bổ sung tests cho uncovered paths:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eChạy pytest --cov=cart --cov-report=term-missing và xem kết quả.\nViết thêm test cases cho những dòng code chưa được cover.\nTarget: 95% coverage.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eTDD cho edge cases và error handling\u003c\/h2\u003e\n\u003cp\u003eMột lợi thế lớn của TDD với Claude Code là khả năng sinh edge case tests mà bạn có thể bỏ sót:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003eĐọc implementation hiện tại trong cart.py và viết thêm edge case tests:\n1. Concurrent modifications (nếu applicable)\n2. Unicode trong tên sản phẩm\n3. Số tiền rất lớn (overflow check)\n4. Empty string cho tên sản phẩm\n5. Floating point precision cho giá tiền\n6. Áp dụng 2 mã giảm giá liên tiếp\n\nMỗi test case cần docstring giải thích tại sao edge case này quan trọng.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eRefactoring an toàn với test safety net\u003c\/h2\u003e\n\u003cp\u003eĐây là lúc giá trị của TDD thực sự tỏa sáng. Khi có đủ tests, bạn có thể yêu cầu Claude Code refactor mạnh tay mà không sợ break functionality:\u003c\/p\u003e\n\n\u003ch3\u003eVí dụ refactoring scenarios:\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Scenario 1: Tách module\nRefactor cart.py thành cấu trúc module:\n- models\/product.py (Product dataclass)\n- models\/discount.py (DiscountCode class)\n- services\/cart_service.py (ShoppingCart logic)\n- services\/pricing_service.py (tính giá logic)\nCập nhật imports trong test_cart.py.\nChạy tests để xác nhận không break gì.\n\n# Scenario 2: Đổi data structure\nHiện tại items dùng list. Đổi sang dict với product name\nlàm key để tối ưu lookup performance.\nGiữ API bên ngoài không đổi.\nChạy tests sau khi refactor.\n\n# Scenario 3: Thêm persistence\nThêm khả năng serialize\/deserialize cart sang JSON.\nViết tests trước cho serialize\/deserialize,\nrồi implement.\nTests cũ phải vẫn pass.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eReal project walkthrough: API endpoint với TDD\u003c\/h2\u003e\n\u003cp\u003eDưới đây là walkthrough đầy đủ cho một tình huống thực tế: xây dựng API endpoint quản lý todo list.\u003c\/p\u003e\n\n\u003ch3\u003ePhase 1: Viết API tests trước\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eTôi cần xây dựng REST API cho todo list với FastAPI:\n- GET \/todos - Lấy danh sách todos (hỗ trợ filter by status)\n- POST \/todos - Tạo todo mới\n- PUT \/todos\/{id} - Cập nhật todo\n- DELETE \/todos\/{id} - Xóa todo\n- PATCH \/todos\/{id}\/complete - Đánh dấu hoàn thành\n\nBusiness rules:\n- Title không được rỗng, tối đa 200 ký tự\n- Không xóa được todo đã hoàn thành\n- Completed todo không thể chuyển về incomplete\n\nViết integration tests bằng pytest + httpx cho FastAPI TestClient.\nChưa viết API code.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003ePhase 2: Implement từng endpoint\u003c\/h3\u003e\n\u003cp\u003eThay vì implement tất cả cùng lúc, áp dụng TDD incremental:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# Lần 1: Chỉ implement POST \/todos\n# Chạy tests -\u0026gt; chỉ test POST pass, còn lại fail (expected)\n\n# Lần 2: Implement GET \/todos\n# Chạy tests -\u0026gt; POST + GET pass\n\n# Lần 3: Implement PUT, DELETE, PATCH\n# Chạy tests -\u0026gt; tất cả pass\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003ePhase 3: Refactor với confidence\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eRefactor todo API:\n1. Tách route handlers ra khỏi business logic\n2. Thêm dependency injection cho database layer\n3. Implement repository pattern\n4. Tất cả tests phải pass sau mỗi bước refactor\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eTips thực hành TDD với Claude Code\u003c\/h2\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTest naming convention:\u003c\/strong\u003e Yêu cầu Claude Code dùng format \"test_should_[expected behavior]_when_[condition]\" để test names tự giải thích\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eArrange-Act-Assert:\u003c\/strong\u003e Mỗi test nên theo cấu trúc rõ ràng: setup, execute, verify. Yêu cầu Claude Code tách 3 phần bằng comment hoặc blank lines\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eMột test, một assertion:\u003c\/strong\u003e Mỗi test nên kiểm tra một behavior cụ thể. Nếu test fail, bạn biết ngay chính xác cái gì hỏng\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eTest independence:\u003c\/strong\u003e Tests không được phụ thuộc nhau. Dùng setup\/teardown (beforeEach, setUp) để đảm bảo mỗi test bắt đầu từ clean state\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eĐừng test implementation:\u003c\/strong\u003e Test behavior, không test cách code được viết. Điều này cho phép refactor thoải mái mà không phải sửa tests\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eCommit sau mỗi Green:\u003c\/strong\u003e Sau khi tests pass, commit ngay. Điều này cho phép bạn rollback bất kỳ lúc nào về trạng thái \"all tests pass\"\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eCLAUDE.md configuration cho TDD workflow\u003c\/h2\u003e\n\u003cp\u003eCấu hình CLAUDE.md để Claude Code tự động chạy tests sau mỗi lần sửa code:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003e# CLAUDE.md\n\n## Testing\n- Luôn chạy tests sau khi sửa code\n- Python: pytest -v --tb=short\n- JavaScript: npx jest --verbose\n- Target coverage: 90%+\n- Viết test trước khi viết implementation\n- Mỗi function phải có ít nhất: happy path test,\n  error case test, edge case test\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eKết hợp TDD với Claude Code Plan Mode\u003c\/h2\u003e\n\u003cp\u003eVới tính năng Plan Mode của Claude Code, bạn có thể lên kế hoạch TDD cho cả feature lớn:\u003c\/p\u003e\n\u003cpre\u003e\u003ccode\u003ePlan mode: Lên kế hoạch TDD cho tính năng \"user authentication\":\n\n1. Liệt kê tất cả components cần implement\n2. Với mỗi component, liệt kê test cases cần viết\n3. Sắp xếp thứ tự implement (dependency order)\n4. Estimate số test cases và LOC cho mỗi component\n\nChưa viết code, chỉ planning.\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eTổng kết\u003c\/h2\u003e\n\u003cp\u003eTDD với Claude Code tạo ra một vòng phản hồi nhanh và đáng tin cậy. Tests trở thành specification sống -- luôn đúng, luôn được cập nhật, và luôn có thể verify tự động. Bạn viết requirements dưới dạng tests, Claude Code biến tests thành implementation, và cả hai cùng đảm bảo chất lượng qua mỗi iteration. Bắt đầu bằng một module nhỏ, làm quen với chu trình Red-Green-Refactor, và dần áp dụng cho toàn bộ dự án. Khi đã có thói quen, bạn sẽ không muốn quay lại cách viết code trước rồi viết test sau nữa.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47730150867156,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/test-driven-development-v_i-claude-code-vi_t-test-tr_c-code-sau.jpg?v=1774715554","url":"https:\/\/claude.vn\/products\/test-driven-development-v%e1%bb%9bi-claude-code-vi%e1%ba%bft-test-tr%c6%b0%e1%bb%9bc-code-sau","provider":"CLAUDE.VN","version":"1.0","type":"link"}