TDD vs BDD: Key Differences, One Feature Built Both Ways and When to Use Each
TDD giúp các nhà phát triển thiết kế mã chính xác. BDD helps a whole team agree on what the software should do. See the same discount feature built with each approach, and learn how the two work together.
- TDD có nghĩa là trước tiên viết một bài kiểm tra thất bại, sau đó chỉ cần đủ mã để vượt qua, sau đó dọn dẹp.
- BDD mô tả hành vi bằng ngôn ngữ chung, thường là Đưa ra, Khi nào, Sau đó.
- TDD chủ yếu hướng dẫn thiết kế code. BDD chủ yếu xây dựng sự hiểu biết chung.
- Họ không phải là đối thủ của nhau và nhiều nhóm sử dụng BDD cho các tính năng và TDD cho các chi tiết bên dưới.
The TDD vs BDD debate often gets framed as choosing a side. Trong thực tế, họ trả lời các câu hỏi khác nhau. Phát triển theo hướng thử nghiệm hỏi liệu mã có thực hiện đúng những gì nhà phát triển dự định hay không, trong khi phát triển theo hướng hành vi hỏi liệu nhóm có đồng ý về những gì phần mềm nên làm hay không.
Để tạo nên sự khác biệt cụ thể, chúng tôi xây dựng một tính năng giảm giá nhỏ cho mỗi phương pháp. Chúng tôi đã chạy mã trên Python 3.13, sử dụng thư viện chuẩn và kết quả bên dưới là từ lần chạy đó. Phần BDD sử dụng một đường chạy nhỏ của riêng chúng tôi để hiển thị cơ chế, do đó, một dự án thực sự sẽ sử dụng một công cụ như Cucumber hoặc Behavior để thay thế.
Câu trả lời ngắn gọn
Test-driven development (TDD) is a coding practice. You write a small failing test, make it pass with the simplest code, and then improve the design. Các thử nghiệm được viết bằng mã và chủ yếu được thực hiện bởi các nhà phát triển. Kent Beck đã phổ biến phương pháp này.
Phát triển theo định hướng hành vi (BDD) là một phương pháp cộng tác bắt nguồn từ TDD và Dan North đã giới thiệu thuật ngữ này. Các nhà phát triển, người thử nghiệm và doanh nhân đồng ý về các ví dụ về cách hệ thống hoạt động, được viết bằng ngôn ngữ đơn giản và những ví dụ đó sau đó có thể được tự động hóa.
TDD và BDD cạnh nhau
| Khía cạnh | TDD | BDD |
|---|---|---|
| Trọng tâm chính | Mã chính xác, được thiết kế tốt | Chia sẻ hiểu biết về hành vi |
| Viết bằng | Ngôn ngữ lập trình | Ngôn ngữ đơn giản, chẳng hạn như Gherkin |
| Được viết bởi | Nhà phát triển | Doanh nghiệp, QA và nhà phát triển cùng nhau |
| Có thể đọc được bởi | Nhà phát triển | Mọi người trong đội |
| Mức độ điển hình | Đơn vị | Tính năng hoặc sự chấp nhận |
| Điểm xuất phát | Một cuộc thử nghiệm thất bại | Cuộc trò chuyện với các ví dụ |
| chu kỳ | Đỏ, xanh lá cây, tái cấu trúc | Khám phá, xây dựng, tự động hóa |
| Công cụ thông dụng | pytest, JUnit, Jest | Dưa chuột, cư xử, SpecFlow, Reqnroll |
tính năng
Đây là quy tắc kinh doanh mà chúng tôi sẽ thực hiện: Đơn hàng từ 1000 trở lên được giảm giá 10%. Thành viên được giảm thêm 5% cho những đơn hàng đó.
Điều đó nghe có vẻ rõ ràng, nhưng nó đã mơ hồ rồi. Thành viên chi 500 có được giảm giá 5% không? Câu nói “những mệnh lệnh đó”, vì vậy chúng tôi cho là không. Hãy ghi nhớ điều đó vì đây chính xác là loại câu hỏi mà BDD được thiết kế để đưa ra sớm.
Xây dựng nó với TDD
TDD tuân theo một vòng lặp ngắn được gọi là red, green, refactor:
| Bước | Bạn làm gì |
|---|---|
| màu đỏ | Viết một bài kiểm tra cho hành vi chưa tồn tại và xem nó thất bại |
| màu xanh lá cây | Viết mã đơn giản nhất để vượt qua |
| Tái cấu trúc | Cải thiện mã, giữ cho các bài kiểm tra luôn xanh |
Đây là các thử nghiệm của chúng tôi, với một thử nghiệm cho mỗi quy tắc:
class DiscountTests(unittest.TestCase):
def test_no_discount_below_1000(self):
self.assertEqual(discount(999, False), 0)
def test_ten_percent_from_1000(self):
self.assertEqual(discount(1000, False), 100)
def test_member_gets_extra_five_percent(self):
self.assertEqual(discount(1000, True), 150)
Sau đó, chúng tôi viết mã thành ba giai đoạn, chạy thử nghiệm sau mỗi giai đoạn. Phiên TDD nghiêm ngặt sẽ thêm một bài kiểm tra mỗi lần và điều này cho thấy tiến trình tương tự:
Giai đoạn 1: điều đơn giản nhất
def discount(total, is_member):
return 0
# 3 tests, 2 failed
# test_member_gets_extra_five_percent
# test_ten_percent_from_1000
Trả về số 0 trông có vẻ ngớ ngẩn, nhưng nó chứng tỏ các thử nghiệm thực sự có thể thất bại và thử nghiệm duy nhất mà nó vượt qua là quy tắc “không giảm giá”.
Giai đoạn 2: thêm ngưỡng
def discount(total, is_member):
if total >= 1000:
return total * 0.1
return 0
# 3 tests, 1 failed
# test_member_gets_extra_five_percent
Giai đoạn 3: thêm thành viên và dọn dẹp
def discount(total, is_member):
rate = 0.1 if total >= 1000 else 0
if is_member:
rate += 0.05
return round(total * rate, 2)
# 3 tests, 0 failed
Cả ba đều vượt qua. Nhưng hãy đọc kỹ phiên bản này. Nó mang lại cho thành viên chi 500 đô la mức giảm giá 5%, đây không phải là điều chúng tôi cho rằng quy tắc này có nghĩa. Không có bài kiểm tra nào đề cập đến trường hợp đó, vì vậy không có gì gắn cờ nó. TDD chỉ bảo vệ hành vi mà bạn nghĩ sẽ kiểm tra. Đó là một hạn chế thực sự và lý do khiến các cuộc trò chuyện về ví dụ lại quan trọng đến vậy.
Tại sao chu kỳ lại quan trọng
- Màu đỏ chứng tỏ rằng thử nghiệm có hiệu quả. Một thử nghiệm chưa bao giờ thất bại có thể không thử nghiệm được bất cứ điều gì.
- Màu xanh lá cây giúp bạn tập trung. Bạn chỉ viết mã cần thiết, điều này giúp ngăn chặn kỹ thuật quá mức.
- Refactor giữ chất lượng cao. Các thử nghiệm mang lại cho bạn sự tự tin để thay đổi thiết kế một cách an toàn.
- Nó ảnh hưởng đến thiết kế. Mã khó kiểm tra thường được liên kết quá chặt chẽ và bạn sẽ nhận thấy ngay.
Hướng dẫn pytest của chúng tôi cho thấy một cách thuận tiện hơn để viết các bài kiểm tra như thế này và phân tích giá trị biên giải thích lý do chúng tôi chọn 999 và 1000 làm giá trị.
Xây dựng nó với BDD
BDD bắt đầu bằng cuộc trò chuyện chứ không phải bằng công cụ. Thông thường, đó là một cuộc họp ngắn được gọi là ba người bạn, trong đó một người phụ trách sản phẩm, một nhà phát triển và một người thử nghiệm sẽ thảo luận về các ví dụ cụ thể:
Tester: What about a member spending 500?
Product: Good question. No discount. It has to reach 1000 first.
Dev: And exactly 1000 counts?
Product: Yes, 1000 or more.
Trao đổi đó có thể đã phát hiện ra vấn đề trong mã TDD của chúng tôi. Sau đó, họ ghi lại các ví dụ của mình ở định dạng có cấu trúc được gọi là Gherkin:
Feature: Order discounts
Scenario: Member reaches the discount threshold
Given a member with a cart worth 1000
When they view the amount to pay
Then the amount to pay is 850
Scenario: Member below the threshold gets no discount
Given a member with a cart worth 500
When they view the amount to pay
Then the amount to pay is 500
| Từ khóa | Mục đích |
|---|---|
| Cho | Tình huống bắt đầu |
| Khi nào | Hành động hoặc sự kiện |
| Sau đó | Kết quả mong đợi |
Bất cứ ai trong nhóm đều có thể đọc được điều đó và nói “vâng, điều đó đúng” hoặc “không, điều đó sai”. Ngay cả khi bạn không bao giờ tự động hóa nó thì kịch bản đó cũng đã thực hiện được công việc hữu ích.
Kết nối các bước với mã
Các công cụ kết nối từng dòng ngôn ngữ đơn giản với một hàm, được gọi là định nghĩa bước. Để thể hiện ý tưởng, chúng tôi đã tự viết một phiên bản nhỏ và chạy kịch bản đầu tiên:
@step("a member with a cart worth 1000")
def given(ctx):
ctx["cart"] = Cart(is_member=True)
ctx["cart"].add(1000)
@step("they view the amount to pay")
def when(ctx):
ctx["amount"] = ctx["cart"].payable()
@step("the amount to pay is 850")
def then(ctx):
assert ctx["amount"] == 850
# ok Given a member with a cart worth 1000
# ok When they view the amount to pay
# ok Then the amount to pay is 850
Các công cụ thực tế thêm tham số để một bước duy nhất có thể khớp với cả “1000” và “500”, cộng với báo cáo. Kết quả là tài liệu sống: một mô tả đơn giản về hệ thống sẽ thất bại nặng nề nếu nó không còn đúng nữa.
Mẹo: Giá trị của BDD nằm ở cuộc trò chuyện. Các nhóm có một người viết Gherkin một mình sau khi mã hoàn tất sẽ nhận được chi phí của công cụ mà hầu như không có lợi ích gì.
Viết kịch bản hay
| yếu | Tốt hơn |
|---|---|
Khi tôi nhấp vào nút có id "btn-77" |
Khi tôi áp dụng phiếu giảm giá |
Vậy thì kết quả là đúng |
Khi đó số tiền phải trả là 850 |
| Hai mươi bước trong một kịch bản | Một hành vi cho mỗi kịch bản |
| Mô tả màn hình | Mô tả mục đích kinh doanh |
Các kịch bản mô tả lượt nhấp chuột và mã trường bị hỏng trong mỗi lần thiết kế lại và người đọc doanh nghiệp sẽ ngừng đọc chúng. Giữ chi tiết giao diện người dùng trong mã bước. Các kịch bản này gần giống với các ví dụ trong trường hợp thử nghiệm so với kịch bản thử nghiệm và chúng cũng thường được sử dụng trong thử nghiệm chấp nhận của người dùng.
Sử dụng cả hai cùng nhau
Cả hai đều phù hợp một cách tự nhiên với quy trình làm việc “từ ngoài vào trong”:
- Nhóm thống nhất về hành vi và viết kịch bản BDD. Nó thất bại, bởi vì chưa có gì tồn tại.
- Nhà phát triển sử dụng TDD để xây dựng các đơn vị nhỏ cần thiết.
- Các bài kiểm tra đơn vị đã vượt qua và cuối cùng kịch bản cũng vậy.
- Mọi thứ đều chạy trong đường dẫn CI/CD của bạn.
Giữ mỗi thứ ở mức phù hợp. Bạn nên có một vài kịch bản cho các hành vi quan trọng và nhiều bài kiểm tra đơn vị nhanh để biết chi tiết. Hướng dẫn của chúng tôi về thử nghiệm đơn vị và tích hợp đề cập đến sự cân bằng.
Công cụ
| Ngôn ngữ | TDD (thử nghiệm đơn vị) | BDD |
|---|---|---|
| Python | pytest, nhỏ nhất | cư xử, pytest-bdd |
| JavaScript | Jest, Vitest | Cucumber.js |
| Java | JUnit, TestNG | Dưa chuột-JVM |
| .NET | xUnit, NUnit | Reqnroll, SpecFlow |
Khi nào nên sử dụng mỗi
TDD phù hợp khi:
- Bạn đang viết logic phức tạp, như định giá, phân tích cú pháp hoặc quyền.
- Bạn đang sửa một lỗi vì trước tiên bạn có thể tái tạo lỗi đó bằng một bài kiểm tra thất bại.
- Bạn đang làm việc với mã sẽ thay đổi thường xuyên.
BDD phù hợp khi:
- Các quy tắc kinh doanh rất phức tạp và sự hiểu lầm sẽ gây ra hậu quả đắt giá.
- Những người không rành về kỹ thuật thực sự sẽ đọc và thảo luận về các tình huống.
- Một số nhóm cần một định nghĩa chung về cách thức hoạt động của một cái gì đó.
Có thể là quá mức cần thiết khi: bạn đang viết một nguyên mẫu dùng một lần hoặc khám phá một ý tưởng kỹ thuật không rõ ràng. Nhiều nhà phát triển tăng đột biến trước, sau đó viết lại bằng các bài kiểm tra. Công cụ BDD cũng là một khoản đầu tư tồi nếu không có ai ngoài bộ phận kỹ thuật xem xét các kịch bản. Trong trường hợp đó, các bài kiểm tra đơn giản với tên hay sẽ đơn giản hơn.
Những lỗi thường gặp
- Coi BDD như một công cụ kiểm tra. Dưa chuột không có sự hợp tác chỉ là một cách dài dòng để viết bài kiểm tra.
- Việc triển khai thử nghiệm trong TDD. Các thử nghiệm gắn với chi tiết riêng tư bị gián đoạn trong mỗi lần tái cấu trúc.
- Bỏ qua bước tái cấu trúc. Bạn vượt qua được các bài kiểm tra do mã lộn xộn.
- Viết bài kiểm tra sau mã và gọi nó là TDD. Điều đó vẫn hữu ích nhưng bạn sẽ mất phản hồi về thiết kế.
- Viết quá nhiều kịch bản giao diện người dùng. Các kịch bản này trở nên chậm và không ổn định. Xem kiểm tra bong tróc.
- Theo đuổi số lượng phù hợp. Mức độ phù hợp tốt vẫn bỏ sót những hành vi không ai xem xét, như ví dụ của chúng tôi cho thấy.
Lời kết
TDD giúp bạn xây dựng đúng thứ và BDD giúp bạn xây dựng thứ đúng đắn. Ví dụ giảm giá của chúng tôi cho thấy cả hai mặt: mã đã vượt qua tất cả các thử nghiệm, tuy nhiên một cuộc trò chuyện đơn giản đã tiết lộ một giả định sai. Hãy thử nó trên tính năng nhỏ tiếp theo của bạn. Viết ba ví dụ cụ thể với một đồng nghiệp trước bất kỳ mã nào, sau đó xây dựng nó từng lần một thử nghiệm thất bại.
Câu hỏi thường gặp
Sự khác biệt chính giữa TDD và BDD là gì?
TDD tập trung vào việc các nhà phát triển viết các bài kiểm tra mã không thành công trước khi triển khai. BDD tập trung vào việc cả nhóm thống nhất về hành vi bằng cách sử dụng các ví dụ đơn giản.
Bạn có thể sử dụng TDD và BDD cùng nhau không?
Có, và nhiều đội làm như vậy. Các kịch bản BDD xác định các tính năng từ bên ngoài và TDD điều khiển các đơn vị mã nhỏ hơn bên dưới chúng.
BDD có tốt hơn TDD không?
Về tổng thể thì không tốt hơn vì chúng giải quyết được các vấn đề khác nhau. BDD giúp chia sẻ sự hiểu biết và TDD giúp thiết kế mã và phản hồi nhanh.
Gherkin là gì?
Gherkin là một định dạng ngôn ngữ đơn giản có cấu trúc để viết kịch bản, sử dụng các từ khóa như Given, When và Then. Các công cụ như Cucumber có thể chạy nó.
TDD có làm chậm sự phát triển không?
Lúc đầu có thể cảm thấy chậm hơn, đặc biệt là trong khi học. Các nhóm thường lấy lại thời gian nhờ ít lỗi hơn và thay đổi an toàn hơn, mặc dù hiệu quả thay đổi tùy theo dự án.
Tiếp tục học hỏi
Cần trợ giúp về kiểm tra, WordPress hoặc SEO?
Chúng tôi giúp các nhóm phát triển phần mềm tốt hơn và phát triển trang web của họ. Hãy cho chúng tôi biết bạn đang làm gì và chúng tôi sẽ đề xuất bước tiếp theo rõ ràng.
Thêm hướng dẫn về thử nghiệm, AI và phát triển hiện đại
Bạn thích bài viết này? Chọn một chủ đề và chuyển ngay sang phần hướng dẫn mới nhất của chúng tôi.
Giải thích về ảo giác AI: Tại sao các mô hình ngôn ngữ lại tạo nên mọi thứ và cách giảm thiểu nó
Ảo giác AI đã giải thích: chúng là gì, tại sao mô hình ngôn ngữ tạo ra chúng, các loại phổ biến, cách giảm thiểu chúng bằng cách căn cứ và nhắc nhở …
Đọc bài viết →DevOpsGiải thích về các biến môi trường: Tệp .env, bí mật và cấu hình được thực hiện đúng
Các biến môi trường được giải thích bằng các ví dụ Python mà chúng tôi đã chạy: đọc và thiết lập chúng, tại sao mọi giá trị đều là một chuỗi, tệp .e…
Đọc bài viết →Phát triển phần mềmGiải thích về giới hạn tỷ lệ API: Thuật toán, tiêu đề và xử lý lỗi 429
Giới hạn tốc độ API được giải thích bằng mô phỏng mà chúng tôi đã chạy: nhóm mã thông báo, cửa sổ cố định, cửa sổ trượt, 429 phản hồi, tiêu đề giới …
Đọc bài viết →Tự động hóa kiểm thửChế giễu bằng Python: unittest.mock, patch và những sai lầm khiến bài kiểm tra nói dối
Mô phỏng bằng Python với unittest.mock, đã được thử nghiệm: Mock và return_value, side_effect, vá lỗi nơi sử dụng tên, thông số kỹ thuật và thông số…
Đọc bài viết →Kiểm thử phần mềmGiải thích về phạm vi bảo hiểm của mã: Phạm vi tuyến so với chi nhánh và ý nghĩa thực sự 100%
Phạm vi bảo hiểm mã được giải thích bằng cách chạy phạm vi bảo hiểm thực tế: phạm vi bảo hiểm dòng, chi nhánh và điều kiện, đọc báo cáo, lý do tại s…
Đọc bài viết →Cơ sở dữ liệu & SQLDELETE vs TRUNCATE vs DROP trong SQL: Sự khác biệt, khôi phục và thời điểm sử dụng mỗi loại
DELETE vs TRUNCATE vs DROP được giải thích bằng SQL đã được thử nghiệm: mỗi loại loại bỏ, khôi phục, kích hoạt, đặt lại danh tính, tốc độ và Postgre…
Đọc bài viết →Cơ sở dữ liệu & SQLGiải thích về Chế độ xem SQL: Tạo, sử dụng và tránh những cạm bẫy, kèm theo ví dụ
Chế độ xem SQL được giải thích bằng các truy vấn chúng tôi đã chạy: tạo chế độ xem, kết quả trực tiếp, ẩn cột, hiệu suất, chế độ xem có thể cập nhật…
Đọc bài viết →Phát triển phần mềmGiao diện TypeScript so với loại: Sự khác biệt thực sự, được thử nghiệm bằng trình biên dịch
Giao diện TypeScript so với loại, được kiểm tra bằng trình biên dịch thực: cả hai có thể làm gì, chỉ một người có thể làm gì, hợp nhất khai báo, mở …
Đọc bài viết →Phát triển phần mềmGiải thích về Python *args và **kwargs: Hàm linh hoạt với ví dụ rõ ràng
Python *args và **kwargs được giải thích bằng mã chúng tôi đã chạy: đóng gói và giải nén, thứ tự tham số, đối số chỉ từ khóa và chỉ vị trí, trình tr…
Đọc bài viết →Phát triển phần mềmHàm Lambda của Python: Cú pháp, cách sử dụng thực tế và khi nào không nên sử dụng chúng
Các hàm lambda của Python được giải thích bằng mã chúng tôi đã chạy: cú pháp, sắp xếp bằng khóa, bản đồ và bộ lọc, các phần đóng, bẫy liên kết muộn …
Đọc bài viết →Trí tuệ nhân tạoĐại lý AI là gì? Cách chúng hoạt động, với vòng lặp tác nhân đơn giản, bạn có thể chạy
Tác nhân AI là gì? Hướng dẫn đơn giản về vòng lặp tác nhân, công cụ, bộ nhớ và lan can, với một vòng lặp Python nhỏ mà chúng tôi đã chạy, những rủi …
Đọc bài viết →DevOpsGiám sát và Khả năng quan sát: Sự khác biệt thực sự, với nhật ký, số liệu và dấu vết
Giải thích về giám sát và khả năng quan sát: lỗi đã biết và chưa biết, nhật ký, số liệu và dấu vết, hướng dẫn về sự cố thanh toán, tín hiệu vàng, Op…
Đọc bài viết →DevOpsDocker Volumes vs Bind Mounts: Duy trì dữ liệu vùng chứa đúng cách
Giải thích về khối lượng Docker và liên kết gắn kết: tại sao dữ liệu vùng chứa biến mất, khối lượng được đặt tên, liên kết gắn kết, tmpfs, ví dụ về …
Đọc bài viết →Tự động hóa kiểm thửTDD vs BDD: Key Differences, One Feature Built Both Ways and When to Use Each
TDD và BDD được giải thích bằng một tính năng giảm giá được xây dựng theo cả hai cách: tái cấu trúc màu đỏ-xanh với mã chúng tôi đã chạy, kịch bản C…
Đọc bài viết →Phát triển phần mềmGit merge vs rebase: khác biệt, ví dụ và khi nào dùng cái nào
Git merge vs rebase được giải thích bằng đầu ra log thực tế, bảng so sánh, quy tắc vàng của rebase và quy trình làm việc nhóm an toàn.
Đọc bài viết →Phát triển phần mềmList comprehension trong Python: cú pháp, ví dụ và khi nào nên tránh
List comprehension trong Python: cú pháp, lọc dữ liệu, if-else, vòng lặp lồng nhau, thời gian đo thực tế và khi nào vòng lặp for thông thường tốt hơn.
Đọc bài viết →Cơ sở dữ liệu & SQLGiải thích SQL JOIN kèm ví dụ (Inner, Left, Right, Full)
Hiểu SQL JOIN từng bước với bảng và truy vấn mẫu: INNER, LEFT, RIGHT, FULL, CROSS và SELF, cùng lỗi thường gặp và mẹo hiệu năng.
Đọc bài viết →