Given-When-Then là một mẫu cấu trúc để mô tả các kịch bản kiểm thử, được BDD vay mượn từ domain-driven design và điều chỉnh cho Behaviour-Driven Development. Định dạng này chia kịch bản thành ba phần logic: tiền điều kiện (Given), hành động (When) và kết quả mong đợi (Then). Theo Martin Fowler (2023), Given-When-Then không chỉ là một định dạng kiểm thử, mà còn là một công cụ tư duy giúp kỷ luật hóa việc phân tích yêu cầu và thiết kế kịch bản trước khi bắt đầu triển khai.
Những điểm chính
Given-When-Then là một mẫu mô tả hành vi, được Dan North lần đầu tiên xây dựng vào năm 2006 như một phần của phương pháp Behavior-Driven Development. Mẫu này giải quyết vấn đề mô tả kịch bản kiểm thử không có cấu trúc, thường chứa hỗn hợp tiền điều kiện, hành động và xác minh theo thứ tự tùy ý.
Ý tưởng chính của mẫu là phân tách trách nhiệm giữa ba khối. Mỗi khối chịu trách nhiệm cho một khía cạnh của kịch bản: trạng thái trước, sự kiện trong và xác minh sau. Điều này làm cho kịch bản có thể đọc được, có thể xác minh và có thể tự động hóa. Theo một nghiên cứu của các nhà phát triển framework Cucumber (2024), các kịch bản tuân thủ nghiêm ngặt mẫu Given-When-Then giúp thành viên mới trong nhóm hiểu nhanh hơn 42%.
Dan North đã vay mượn ý tưởng cấu trúc ba phần từ công thức kiểm thử trong TDD và phương pháp Test-by-Example (do Brian Marick tạo ra). Marick đề xuất mô tả yêu cầu thông qua các ví dụ (examples) đồng thời đóng vai trò là kiểm thử. Given-When-Then đã chính thức hóa ý tưởng này, biến các ví dụ không có cấu trúc thành một mẫu có thể lặp lại.
Mẫu Given-When-Then không chỉ được áp dụng trong các kịch bản BDD với Gherkin, mà còn trong các kiểm thử đơn vị thông thường với JUnit, XCTest và các framework khác. Các chú thích trong mã nguồn chia kiểm thử thành ba khối là một thực hành phổ biến để cải thiện khả năng đọc của cơ sở kiểm thử. Google khuyến nghị cách tiếp cận này trong cuốn sách «Software Engineering at Google» (2020) của họ.
Mỗi khối của Given-When-Then có ngữ nghĩa được xác định chặt chẽ và các quy tắc điền nội dung. Vi phạm các quy tắc này dẫn đến các kịch bản khó tự động hóa hoặc hiểu.
Khối Given mô tả trạng thái của hệ thống trước khi thực hiện hành động được kiểm thử. Nó bao gồm: các đối tượng hiện có (người dùng, đơn hàng, cài đặt), trạng thái hoạt động (đã xác thực, kết nối mạng) và giá trị dữ liệu ban đầu. Mỗi Given phải có thể xác minh được — nếu trạng thái hệ thống không khớp với Given, kịch bản sẽ bị bỏ qua hoặc môi trường kiểm thử cần được chuẩn bị trước.
Khối When mô tả sự kiện duy nhất kích hoạt hành vi được kiểm thử. Đó có thể là một lời gọi phương thức, nhấp chuột, nhận thông báo hoặc phản hồi từ máy chủ. Quy tắc chính là một When cho mỗi kịch bản. Nếu cần xác minh một chuỗi hành động, hãy tạo các kịch bản riêng biệt, không phải một chuỗi When.
// Given: tạo dữ liệu kiểm thử
val user = User(email = "test@example.com", balance = 500.0)
val product = Product(price = 150.0)
// When: thực hiện hành động
val result = PurchaseUseCase().buy(user, product)
// Then: xác minh kết quả
assertEquals(PurchaseResult.Success, result)
assertEquals(350.0, user.balance)
Khối Then xác minh rằng hệ thống đã chuyển sang trạng thái mong đợi. Điều này bao gồm: giá trị trả về, thay đổi trạng thái của đối tượng, lời gọi dịch vụ bên ngoài (thông qua xác minh mock) và thay đổi giao diện người dùng. Mỗi khối Then có thể chứa nhiều xác minh, nhưng tất cả đều liên quan đến một hành động duy nhất.
Given-When-Then và Arrange-Act-Assert (AAA) là hai biến thể của cùng một mẫu ba phần, nhưng với đối tượng mục tiêu khác nhau. Hiểu sự khác biệt của chúng giúp chọn định dạng phù hợp cho từng nhiệm vụ.
| Khía cạnh | Given-When-Then | Arrange-Act-Assert |
|---|---|---|
| Nguồn gốc | BDD, phân tích kinh doanh | Kiểm thử đơn vị |
| Ngôn ngữ | Tự nhiên (Gherkin) | Mã nguồn (Kotlin, Swift, Java) |
| Đối tượng | Toàn bộ nhóm + khách hàng | Nhà phát triển |
| Mức độ chi tiết | Cấp cao | Chi tiết |
| Tự động hóa | Cucumber, SpecFlow | JUnit, XCTest, Mockito |
Mẫu Given-When-Then là tối ưu cho các kịch bản được thảo luận với khách hàng hoặc nhà phân tích: tiêu chí chấp nhận tính năng, trường hợp sử dụng, kiểm tra hồi quy. Cú pháp Gherkin cho phép viết các kịch bản này mà không cần kiến thức lập trình.
Arrange-Act-Assert là lựa chọn tự nhiên cho các kiểm thử đơn vị xác minh một phương thức hoặc lớp cụ thể. Định dạng AAA không yêu cầu framework bổ sung và hoạt động trong bất kỳ ngôn ngữ lập trình nào. Đối với phát triển iOS, Apple khuyến nghị AAA trong tài liệu XCTest (2024).
Hãy xem các ví dụ thực tế về Given-When-Then bằng Kotlin cho ứng dụng Android. Ví dụ đầu tiên là kiểm thử giỏ hàng sử dụng MockK. Ví dụ thứ hai là kiểm thử logic thông báo đẩy.
class CartTest {
fun `apply discount when total exceeds threshold`() {
// Given
val cart = Cart()
cart.addItem(Item("Laptop", price = 1000.0))
cart.addItem(Item("Mouse", price = 50.0))
val discount = DiscountCalculator(0.1)
// When
val total = discount.applyIfEligible(cart)
// Then
assertEquals(945.0, total)
assertTrue("Discount was not applied", total < 1050.0)
}
}
Ví dụ thứ hai minh họa Given-When-Then với mã bất đồng bộ. Ở đây Given thiết lập trạng thái Firebase Cloud Messaging, When — nhận thông báo đẩy, Then — xác minh xử lý.
class PushNotificationTest {
fun `handle push notification when app in background`() = runTest {
// Given
val prefs = mockk<SharedPreferences>()
every { prefs.getString("token", null) } returns "fcm-token-abc"
val handler = PushHandler(prefs)
// When
val data = RemoteMessage().apply {
putData("type", "order_update")
putData("order_id", "123")
}
val result = handler.handleNotification(data)
// Then
assertEquals(NotificationAction.OpenOrder("123"), result)
}
}
Ví dụ thứ ba là kịch bản BDD bằng Gherkin thể hiện Given-When-Then trong bối cảnh kiểm thử chấp nhận:
Feature: User Authorization
Scenario: User cannot login with expired token
Given the user has an expired refresh token
When they try to access the protected profile screen
Then they should see the login screen
And the app should clear all cached data
Áp dụng hiệu quả Given-When-Then yêu cầu tuân theo một số thực hành đã được kiểm chứng. Chúng đảm bảo khả năng đọc, bảo trì và tự động hóa của các kịch bản.
Quy tắc nghiêm ngặt: một kịch bản — một hành động. Nếu cần xác minh một chuỗi nhiều When, hãy tạo nhiều kịch bản trong đó kết quả của kịch bản trước trở thành tiền điều kiện của kịch bản sau. Điều này làm cho kịch bản trở nên nguyên tử và dễ hiểu.
Given nên mô tả bản chất, không phải các con số cụ thể. Thay vì «Given người dùng Ivanov với số dư 500 rúp» — «Given người dùng có đủ số dư». Dữ liệu cụ thể được chuyển vào Scenario Outline với bảng Examples. Điều này làm cho kịch bản trở nên phổ quát và có thể tái sử dụng.
Tích hợp các kịch bản Given-When-Then vào pipeline tích hợp liên tục biến chúng từ tài liệu thành công cụ bảo vệ chống hồi quy. Mỗi yêu cầu hợp nhất trong dự án di động tự động chạy các kịch bản BDD và chặn hợp nhất nếu ít nhất một kịch bản thất bại.
Các kịch bản BDD với Cucumber cho Android được chạy thông qua tác vụ Gradle ./gradlew cucumber. Cho iOS (Quick/Nimble) — thông qua xcodebuild test. Trong các hệ thống CI (GitHub Actions, GitLab CI, Bitrise), các kiểm thử BDD được thực thi trên trình giả lập hoặc thiết bị thật. Báo cáo được tạo ở định dạng HTML dễ hiểu cho quản lý: kịch bản xanh — đạt, đỏ — thất bại có chỉ dẫn bước.
Các tệp .feature được lưu trữ trong kho lưu trữ bên cạnh mã nguồn và trải qua đánh giá mã. Nhà phân tích tạo yêu cầu hợp nhất với các kịch bản mới trước khi bắt đầu phát triển (BDD-first). Nhà phát triển viết định nghĩa bước và triển khai để các kịch bản này trở nên xanh. Khi tất cả kịch bản đều đạt — chức năng đã sẵn sàng. Cách tiếp cận này, được mô tả trong cuốn sách của Gojko Adzic «Specification by Example» (2011), biến yêu cầu thành một tạo phẩm có thể thực thi.
Câu hỏi thường gặp
Về cấu trúc, có, đó là cùng một mẫu ba phần. Sự khác biệt nằm ở đối tượng: Given-When-Then hướng đến ngôn ngữ kinh doanh và được sử dụng trong BDD với Gherkin, trong khi Arrange-Act-Assert là định dạng kỹ thuật cho kiểm thử đơn vị. Lựa chọn phụ thuộc vào ngữ cảnh và nhóm.
Không có giới hạn, nhưng khuyến nghị không quá 3–5 xác minh cho mỗi Then. Nếu có nhiều xác minh hơn, kịch bản có thể đang kiểm tra quá nhiều trong một hành động. Hãy chia nó thành nhiều kịch bản với các Then khác nhau.
Không. Mẫu này có thể được sử dụng trong bất kỳ framework kiểm thử nào, chỉ cần chia kiểm thử thành ba khối bằng chú thích hoặc dòng trống. Gherkin chỉ cần thiết nếu kịch bản được viết ở định dạng .feature cho Cucumber hoặc SpecFlow.
Khuyến nghị trích xuất các tiền điều kiện lặp lại vào Background (Gherkin) hoặc phương thức @Before (JUnit). Nếu tiền điều kiện phức tạp, hãy sử dụng mẫu Builder để tạo dữ liệu kiểm thử. Điều này giữ cho Given ngắn gọn và dễ đọc.
Không. When là khối bắt buộc mô tả hành động. Nếu kịch bản chỉ xác minh trạng thái mà không có hành động (ví dụ: «khi tải ứng dụng, dữ liệu phải được lưu vào bộ nhớ đệm»), When mô tả trình kích hoạt: «khi ứng dụng khởi động».
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm