Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

TDD & BDD — khái niệm cho QA

Software Testing Fundamentals Bài 42/60

Mở đầu — vì sao bài này quan trọng

Nếu bạn là một tester và từng ngồi họp sprint planning, chắc chắn bạn đã nghe lập trình viên nhắc đến TDD, và có thể nghe Product Owner hoặc BA nhắc đến BDD, Cucumber, Gherkin. Nhiều bạn tester nghe xong thì gật gù cho qua, vì nghĩ rằng: "TDD là chuyện của dev, mình chỉ test thôi." Đó là một hiểu lầm khiến bạn tự đóng cửa với rất nhiều cơ hội nghề nghiệp.

Sự thật là: TDD (Test-Driven Development) và BDD (Behavior-Driven Development) là hai trong số những kỹ thuật định hình cách một team hiện đại làm phần mềm. Bạn không cần phải tự tay viết code TDD, nhưng bạn PHẢI hiểu nó, vì nó quyết định chất lượng của lớp test dưới cùng (unit test) — thứ mà bạn đang dựa vào khi xây chiến lược kiểm thử. Còn BDD thì trực tiếp là sân chơi của bạn: chính tester là người thường viết và bảo trì các kịch bản Gherkin, làm cầu nối giữa yêu cầu nghiệp vụ và kiểm thử tự động.

Ở nhiều công ty tại Việt Nam như FPT Software, KMS Technology, hay các startup fintech ở TP.HCM, tester biết BDD thường được trả lương cao hơn và có cơ hội tiến lên vai trò SDET (Software Development Engineer in Test). Bài học này giúp bạn phân biệt rạch ròi hai khái niệm, hiểu chúng phục vụ mục đích gì, và quan trọng nhất: biết vai trò của một QA nằm ở đâu trong bức tranh đó.

Khái niệm cốt lõi

TDD — Test-Driven Development

TDD là một kỹ thuật lập trình, không phải một loại kiểm thử. Đây là điểm nhiều người nhầm lẫn nhất. Trong TDD, lập trình viên viết test TRƯỚC, rồi mới viết code để làm cho test đó pass. Quy trình xoay quanh một vòng lặp ngắn gọi là Red-Green-Refactor:

  • RED — Viết một test cho chức năng chưa tồn tại. Chạy test, nó fail (màu đỏ) vì code chưa có. Điều này xác nhận test thực sự đang kiểm tra một cái gì đó.
  • GREEN — Viết lượng code tối thiểu vừa đủ để test chuyển sang pass (màu xanh). Không viết dư, không tối ưu sớm.
  • REFACTOR — Giờ khi đã có lưới an toàn (test xanh), dọn dẹp code cho sạch, gọn, dễ đọc mà không làm hỏng test.
Rồi lặp lại vòng này cho chức năng tiếp theo. Mỗi vòng thường chỉ vài phút.

Điểm mấu chốt cần hiểu: TDD tạo ra một tập unit test rất dày và chạy nhanh, gắn chặt với code. Đây chính là tầng đáy của Test Pyramid (bài 6). Khi dev làm TDD nghiêm túc, tầng unit test khỏe, bạn — với tư cách QA — có thể tập trung sức lực vào tầng integration và E2E thay vì phải bắt những lỗi ngớ ngẩn cấp thấp.

TDD được viết bằng chính ngôn ngữ lập trình và framework như JUnit (Java), pytest (Python), Jest (JavaScript). Người đọc test TDD chủ yếu là dev. Test tập trung vào "code làm đúng không" — tức là góc nhìn kỹ thuật, bên trong.

BDD — Behavior-Driven Development

BDD sinh ra từ TDD nhưng nâng lên một tầng trừu tượng. Dan North, người đặt ra thuật ngữ BDD năm 2006, nhận thấy vấn đề lớn của TDD là dev thường không biết "nên bắt đầu test cái gì" và test viết ra khó cho người không rành kỹ thuật đọc hiểu. BDD giải quyết bằng cách chuyển trọng tâm từ "test code" sang "mô tả hành vi mong đợi của hệ thống bằng ngôn ngữ mà cả business, dev và tester đều đọc được".

BDD sử dụng cú pháp Gherkin — một ngôn ngữ gần với tiếng tự nhiên, theo cấu trúc Given-When-Then:

  • Given (Cho trước) — bối cảnh, trạng thái ban đầu của hệ thống.
  • When (Khi) — hành động người dùng thực hiện.
  • Then (Thì) — kết quả mong đợi.
Ví dụ một scenario Gherkin cho tính năng đăng nhập:

Tính năng: Đăng nhập tài khoản
  Scenario: Đăng nhập thành công với thông tin đúng
    Given người dùng đang ở trang đăng nhập
    When người dùng nhập email "an@example.com" và mật khẩu đúng
    Then hệ thống chuyển đến trang chủ
    And hiển thị lời chào "Xin chào An"

Kịch bản này được viết bằng ngôn ngữ ai cũng hiểu, sau đó dev viết "step definitions" — đoạn code gắn từng dòng Given/When/Then với hành động tự động hóa thật (gọi API, click nút trên UI...). Công cụ phổ biến: Cucumber (Java, Ruby, JS), SpecFlow (.NET), Behave (Python).

Bảng so sánh nhanh

Tiêu chíTDDBDD
Trọng tâmCode hoạt động đúng không (kỹ thuật)Hệ thống hành xử đúng nghiệp vụ không
Ngôn ngữNgôn ngữ lập trình (JUnit, pytest)Ngôn ngữ tự nhiên (Gherkin)
Người đọc chínhDevDev, QA, BA, PO
Cú phápAssert, expectGiven-When-Then
Cấp độChủ yếu unitChủ yếu feature/acceptance
Vai trò QAHiểu để đánh giá độ phủTrực tiếp viết & bảo trì scenario
Một cách nhớ ngắn gọn: TDD giúp bạn xây phần mềm đúng cách (build the thing right); BDD giúp bạn xây đúng phần mềm (build the right thing).

Vai trò của QA — điểm quan trọng nhất bài này

Là tester, bạn cần định vị rõ:

Với TDD, bạn không viết code TDD, nhưng bạn cần: đọc được kết quả unit test để biết độ phủ tầng đáy, đặt câu hỏi trong review khi thấy một module quan trọng thiếu unit test, và tránh viết trùng lặp — đừng bỏ công viết E2E để kiểm những case mà unit test đã lo.

Với BDD, bạn thường là người dẫn dắt. Trong các buổi "Three Amigos" (BA + Dev + QA cùng ngồi bàn về một user story), QA là người giỏi nhất trong việc nghĩ ra các scenario ngoài happy path — case biên, case lỗi, dữ liệu bất thường. Bạn biến những suy nghĩ đó thành các scenario Gherkin, và chúng trở thành "living documentation" — tài liệu sống, vừa là đặc tả vừa là test tự động.

Tình huống thực tế

Tình huống 1: Startup fintech ở TP.HCM áp dụng TDD cho module tính lãi suất

Một công ty fintech cho vay tiêu dùng (giả định tên Tikop-Loan) có module tính lãi suất trả góp cực kỳ nhạy cảm: sai một đồng là khách hàng khiếu nại, sai lớn là rủi ro pháp lý. Ban đầu team dev viết code trước, test sau, và trong 3 tháng có 7 bug liên quan đến làm tròn số và ngày tính lãi lọt ra production.

Team lead quyết định áp dụng TDD cho riêng module này. Với mỗi công thức lãi (lãi theo dư nợ gốc, lãi theo dư nợ giảm dần, phí phạt trả chậm), dev viết trước hàng loạt unit test với các con số cụ thể: khoản vay 10 triệu, kỳ hạn 12 tháng, lãi 1.5%/tháng thì kỳ đầu phải trả đúng bao nhiêu. Test fail trước (RED), rồi dev viết công thức cho pass (GREEN), rồi dọn code (REFACTOR).

Kết quả sau 2 tháng: số bug về tính toán ở module này giảm về 0 trên production. QA của team, chị Ngân, không viết những unit test đó, nhưng chị đọc danh sách test case dev đã phủ, phát hiện thiếu case "khách trả trước hạn cả gốc" và "kỳ nghỉ lễ làm dời ngày tính lãi". Chị đề xuất, dev bổ sung hai unit test đó ngay trong sprint.

Bài học: TDD làm tầng đáy vững, nhưng QA vẫn là người bổ sung góc nhìn nghiệp vụ mà dev dễ bỏ sót. QA không cần viết code TDD vẫn tạo ra giá trị lớn thông qua review độ phủ.

Tình huống 2: Team thương mại điện tử dùng BDD để chấm dứt tranh cãi "yêu cầu là gì"

Một sàn TMĐT tầm trung (giả định tên ChợViệt) liên tục gặp cảnh: BA viết yêu cầu trong Word, dev hiểu một kiểu, QA test theo một kiểu khác, đến lúc demo thì PO nói "không, ý tôi không phải vậy". Mỗi tính năng làm lại 2-3 vòng, lãng phí khủng khiếp.

Họ chuyển sang BDD. Với tính năng "áp mã giảm giá", trong buổi Three Amigos, cả nhóm cùng viết ra các scenario Gherkin ngay tại chỗ:

Scenario: Mã giảm giá hết hạn
  Given giỏ hàng có tổng tiền 500.000đ
  And mã "TET2026" đã hết hạn ngày 15/02/2026
  When khách áp mã "TET2026"
  Then hệ thống báo "Mã đã hết hạn"
  And tổng tiền vẫn là 500.000đ

QA của nhóm, anh Dũng, là người đóng góp nhiều scenario nhất: mã áp cho đơn dưới ngưỡng tối thiểu, mã dùng quá số lần cho phép, áp hai mã cùng lúc, mã đúng nhưng viết hoa/thường lẫn lộn. Những scenario này vừa là đặc tả cho dev, vừa được Cucumber tự động hóa thành test chạy trong CI.

Kết quả: số vòng làm lại vì "hiểu sai yêu cầu" giảm từ trung bình 2.5 vòng xuống còn 0.4 vòng mỗi tính năng. Tài liệu Gherkin trở thành nguồn sự thật duy nhất, thay cho file Word hay lỗi thời.

Bài học: BDD không phải công cụ test tự động đơn thuần — giá trị lớn nhất của nó là buộc business, dev, QA thống nhất cách hiểu TRƯỚC khi code. QA là chất xúc tác chính trong cuộc trò chuyện đó.

Tình huống 3: Cái bẫy "BDD giả" tại một team outsourcing

Một team outsourcing ở Đà Nẵng nghe nói BDD hay nên áp dụng. Nhưng họ làm sai: dev viết xong code, QA mới ngồi viết file Gherkin để "cho có", scenario dài lê thê mô tả từng cú click UI thay vì mô tả hành vi. Sau 6 tháng, họ có 400 scenario Cucumber cực kỳ giòn (brittle) — cứ đổi một nút trên UI là hàng chục test đỏ, mất cả ngày sửa. Không ai ngoài QA đọc file Gherkin đó, business chẳng bao giờ liếc tới.

Cuối cùng team bỏ luôn Cucumber, quay về viết test tự động thuần bằng Selenium + code. Họ nhận ra: họ chưa từng làm BDD thật, họ chỉ dùng cú pháp Gherkin cho một bộ E2E test thông thường, gánh thêm chi phí step definitions mà không thu về lợi ích "giao tiếp chung".

Bài học: BDD chỉ có giá trị khi được làm từ đầu, có sự tham gia của business, và scenario mô tả HÀNH VI (cái gì) chứ không phải THAO TÁC (làm thế nào). Dùng Gherkin sau khi code xong chỉ để tự động hóa là "BDD giả" — tốn công vô ích.

Hướng dẫn từng bước

Đây là cách một QA tham gia hiệu quả vào môi trường TDD/BDD:

Bước 1 — Hiểu team đang ở đâu. Hỏi rõ: dev có làm TDD thật không, hay chỉ viết test sau? Team có dùng Cucumber/SpecFlow không? Ai viết Gherkin? Đừng giả định.

Bước 2 — Với TDD, học đọc kết quả unit test. Mở báo cáo coverage (JaCoCo cho Java, coverage.py cho Python). Xác định module nào coverage thấp và rủi ro cao. Đây là đầu vào quý giá cho chiến lược test của bạn: nơi unit test yếu là nơi bạn cần test thủ công/tích hợp kỹ hơn.

Bước 3 — Tham gia Three Amigos cho mỗi user story. Trước khi dev code, ngồi cùng BA và dev. Nhiệm vụ của bạn: đặt câu hỏi "còn trường hợp nào nữa?" — dữ liệu rỗng, giá trị biên, lỗi mạng, người dùng không có quyền.

Bước 4 — Viết scenario Gherkin đúng chuẩn. Mỗi scenario tập trung một hành vi. Given mô tả trạng thái, không mô tả thao tác. When chỉ một hành động chính. Then kiểm tra kết quả quan sát được, không kiểm tra chi tiết kỹ thuật nội bộ.

Bước 5 — Phối hợp với dev để tạo step definitions. Bạn viết scenario, dev (hoặc bạn nếu là SDET) viết code gắn từng bước với hành động thật. Ưu tiên tái sử dụng các step chung.

Bước 6 — Đưa vào CI. Scenario BDD chạy tự động mỗi lần build. Khi một scenario đỏ, nó nói rõ hành vi nào bị hỏng bằng ngôn ngữ nghiệp vụ — dễ chẩn đoán hơn nhiều so với test kỹ thuật thuần.

Bước 7 — Bảo trì như tài sản sống. Khi yêu cầu đổi, cập nhật scenario TRƯỚC, coi nó là đặc tả mới, rồi mới sửa code. Đừng để Gherkin lỗi thời.

Lỗi thường gặp & mẹo

Lỗi 1: Nhầm TDD là một cấp độ kiểm thử. TDD là kỹ thuật lập trình. Nói "chúng ta cần thêm giai đoạn TDD trong quy trình test" là sai bản chất.

Lỗi 2: Viết scenario Gherkin mô tả thao tác UI. Ví dụ sai: "When tôi click nút màu xanh ở góc phải, When tôi cuộn xuống...". Đúng phải là: "When khách hoàn tất đặt hàng". Scenario phải sống lâu hơn giao diện.

Lỗi 3: Một scenario ôm quá nhiều thứ. Nhồi 15 dòng When-Then vào một scenario khiến khi nó đỏ bạn không biết cái gì hỏng. Mỗi scenario một hành vi.

Lỗi 4: Coi BDD chỉ là công cụ automation. Nếu business không bao giờ đọc Gherkin của bạn, bạn đã mất 80% giá trị của BDD và chỉ gánh thêm chi phí.

Lỗi 5: QA đứng ngoài TDD/BDD. Nghĩ "đây là việc của dev" khiến bạn tụt hậu. Hiểu tầng unit test giúp bạn không test trùng và định vị đúng công sức.

Mẹo: Dùng từ khóa Background trong Gherkin để gom các bước Given lặp lại, và Scenario Outline với bảng Examples để chạy cùng một hành vi với nhiều bộ dữ liệu — cực gọn cho các case kiểu equivalence partitioning.

Mẹo: Khi mới học, tập viết Gherkin cho những tính năng bạn dùng hàng ngày (đăng nhập MoMo, đặt Grab) để rèn tư duy Given-When-Then trước khi áp vào dự án thật.

Bài tập thực hành

  • Phân biệt khái niệm: Viết ra bằng lời của bạn sự khác nhau giữa TDD và BDD theo ba trục: người đọc test, ngôn ngữ dùng, mục đích chính. Không nhìn lại bài.
  • Vẽ vòng Red-Green-Refactor: Lấy một hàm đơn giản (ví dụ: kiểm tra một số điện thoại Việt Nam có hợp lệ không). Mô tả bạn sẽ viết test gì ở bước RED, code tối thiểu gì ở GREEN, và có thể refactor gì.
  • Viết scenario Gherkin: Cho tính năng "rút tiền từ ví điện tử". Viết ít nhất 4 scenario theo Given-When-Then: rút thành công, rút vượt số dư, rút dưới mức tối thiểu, và rút khi tài khoản chưa xác thực. Dùng số tiền cụ thể.
  • Nhận diện lỗi: Đọc lại Tình huống 3. Liệt kê 3 dấu hiệu cho thấy team đó đang làm "BDD giả", và đề xuất cách sửa cho mỗi dấu hiệu.
  • Áp dụng vai trò QA: Giả sử team bạn có dev làm TDD tốt cho tầng logic. Bạn nên tập trung công sức test thủ công vào đâu để tránh trùng lặp? Viết 3 câu trả lời.

Tóm tắt

TDD và BDD không phải là loại kiểm thử — chúng là hai kỹ thuật phát triển phần mềm mà mọi QA hiện đại cần hiểu. TDD (Red-Green-Refactor) là kỹ thuật của dev, tạo ra tầng unit test dày và nhanh, giúp "xây phần mềm đúng cách"; vai trò của QA là đọc hiểu độ phủ để không test trùng và bổ sung góc nhìn nghiệp vụ còn thiếu. BDD (Given-When-Then, Gherkin) nâng test lên ngôn ngữ tự nhiên để business, dev và QA cùng hiểu, giúp "xây đúng phần mềm"; và đây chính là sân chơi mà QA thường dẫn dắt qua các buổi Three Amigos và việc viết, bảo trì scenario.

Giá trị cốt lõi của cả hai không nằm ở công cụ, mà ở tư duy: viết ra kỳ vọng TRƯỚC, dùng nó làm kim chỉ nam cho code. Với BDD, hãy nhớ nó chỉ hiệu quả khi làm từ đầu, có business tham gia, và scenario mô tả hành vi chứ không phải thao tác. Nắm vững hai khái niệm này, bạn không còn là người "chỉ test", mà trở thành mắt xích chất lượng trong toàn bộ vòng đời phát triển — bước đệm vững chắc để tiến tới vai trò SDET.