Given-When-Then เป็นรูปแบบโครงสร้างสำหรับอธิบายสถานการณ์การทดสอบ ที่ BDD ยืมมาจาก domain-driven design และปรับให้เข้ากับ Behaviour-Driven Development รูปแบบนี้แบ่งสถานการณ์ออกเป็นสามส่วนเชิงตรรกะ: เงื่อนไขเบื้องต้น (Given), การกระทำ (When) และผลลัพธ์ที่คาดหวัง (Then) ตามที่ Martin Fowler (2023) กล่าว Given-When-Then ไม่ใช่แค่รูปแบบการทดสอบ แต่เป็นเครื่องมือทางความคิดที่ทำให้การวิเคราะห์ความต้องการและการออกแบบสถานการณ์เป็นระบบก่อนเริ่มการนำไปใช้
ประเด็นสำคัญ
Given-When-Then เป็นรูปแบบการอธิบายพฤติกรรมที่คิดค้นครั้งแรกโดย Dan North ในปี 2006 ซึ่งเป็นส่วนหนึ่งของวิธีการ Behavior-Driven Development รูปแบบนี้แก้ปัญหาการอธิบายสถานการณ์ทดสอบที่ไม่มีโครงสร้าง ซึ่งมักประกอบด้วยเงื่อนไขเบื้องต้น การกระทำ และการตรวจสอบปะปนกันในลำดับตามอำเภอใจ
แนวคิดหลักของรูปแบบคือ การแยกความรับผิดชอบ ระหว่างสามบล็อก แต่ละบล็อกรับผิดชอบเพียงแง่มุมเดียวของสถานการณ์: สถานะก่อน, เหตุการณ์ระหว่าง, และการตรวจสอบหลัง สิ่งนี้ทำให้สถานการณ์สามารถอ่านได้ ตรวจสอบได้ และทำให้เป็นอัตโนมัติได้ จากการศึกษาของผู้พัฒนาเฟรมเวิร์ก Cucumber (2024) สถานการณ์ที่ปฏิบัติตามรูปแบบ Given-When-Then อย่างเคร่งครัดใช้เวลาทำความเข้าใจน้อยลง 42% สำหรับสมาชิกทีมใหม่
Dan North ยืมแนวคิดของโครงสร้างสามส่วนมาจาก การกำหนดสูตรการทดสอบใน TDD และวิธีการ Test-by-Example (สร้างโดย Brian Marick) Marick เสนอให้อธิบายความต้องการผ่านตัวอย่าง (examples) ที่ทำหน้าที่เป็นการทดสอบไปพร้อมกัน Given-When-Then ทำให้แนวคิดนี้เป็นทางการ เปลี่ยนตัวอย่างที่ไม่มีโครงสร้างให้เป็นรูปแบบที่ทำซ้ำได้
รูปแบบ Given-When-Then ไม่เพียงแต่ใช้ในสถานการณ์ BDD ด้วย Gherkin เท่านั้น แต่ยังใช้ในการทดสอบหน่วยทั่วไปด้วย JUnit, XCTest และเฟรมเวิร์กอื่นๆ ความคิดเห็นในโค้ดที่แบ่งการทดสอบออกเป็นสามบล็อกเป็นแนวทางปฏิบัติทั่วไปเพื่อปรับปรุงความสามารถในการอ่านของฐานการทดสอบ Google แนะนำแนวทางนี้ในหนังสือ «Software Engineering at Google» (2020)
แต่ละบล็อกของ Given-When-Then มีความหมายเชิงความหมายที่กำหนดไว้อย่างเคร่งครัดและกฎการเติมเนื้อหา การละเมิดกฎเหล่านี้จะนำไปสู่สถานการณ์ที่ยากต่อการทำให้เป็นอัตโนมัติหรือเข้าใจ
บล็อก Given อธิบายสถานะของระบบก่อนดำเนินการกระทำที่ถูกทดสอบ รวมถึง: ออบเจกต์ที่มีอยู่ (ผู้ใช้, คำสั่งซื้อ, การตั้งค่า), สถานะที่ใช้งานอยู่ (รับรองความถูกต้อง, เชื่อมต่อเครือข่าย) และค่าเริ่มต้นของข้อมูล แต่ละ Given จะต้องสามารถตรวจสอบได้ — หากสถานะของระบบไม่ตรงกับ Given ควรข้ามสถานการณ์นั้นหรือเตรียมสภาพแวดล้อมการทดสอบล่วงหน้า
บล็อก When อธิบายเหตุการณ์เดียวที่เริ่มต้นพฤติกรรมที่ถูกทดสอบ อาจเป็นการเรียกเมธอด, การคลิกปุ่ม, การรับการแจ้งเตือนหรือการตอบสนองจากเซิร์ฟเวอร์ กฎสำคัญคือ หนึ่ง When ต่อหนึ่งสถานการณ์ หากจำเป็นต้องตรวจสอบลำดับของการกระทำ ให้สร้างสถานการณ์แยกต่างหาก ไม่ใช่ห่วงโซ่ของ When
// Given: สร้างข้อมูลทดสอบ
val user = User(email = "test@example.com", balance = 500.0)
val product = Product(price = 150.0)
// When: ดำเนินการกระทำ
val result = PurchaseUseCase().buy(user, product)
// Then: ตรวจสอบผลลัพธ์
assertEquals(PurchaseResult.Success, result)
assertEquals(350.0, user.balance)
บล็อก Then ตรวจสอบว่าระบบได้เปลี่ยนไปสู่สถานะที่คาดหวังแล้ว ซึ่งรวมถึง: ค่าที่ส่งกลับ, การเปลี่ยนแปลงสถานะของออบเจกต์, การเรียกใช้บริการภายนอก (ผ่านการตรวจสอบ mock) และการเปลี่ยนแปลง UI แต่ละบล็อก Then สามารถมีการตรวจสอบหลายรายการ แต่ทั้งหมดเกี่ยวข้องกับการกระทำเดียว
Given-When-Then และ Arrange-Act-Assert (AAA) เป็นสองรูปแบบของรูปแบบสามส่วนเดียวกัน แต่มีกลุ่มเป้าหมายที่แตกต่างกัน การเข้าใจความแตกต่างของพวกเขาช่วยเลือกรูปแบบที่เหมาะสมสำหรับงานแต่ละอย่าง
| แง่มุม | Given-When-Then | Arrange-Act-Assert |
|---|---|---|
| ที่มา | BDD, การวิเคราะห์ธุรกิจ | การทดสอบหน่วย |
| ภาษา | ธรรมชาติ (Gherkin) | โค้ด (Kotlin, Swift, Java) |
| กลุ่มเป้าหมาย | ทั้งทีม + ลูกค้า | นักพัฒนา |
| ระดับรายละเอียด | ระดับสูง | ละเอียด |
| การทำให้เป็นอัตโนมัติ | Cucumber, SpecFlow | JUnit, XCTest, Mockito |
รูปแบบ Given-When-Then เหมาะสมที่สุดสำหรับสถานการณ์ที่หารือกับลูกค้าหรือนักวิเคราะห์: เกณฑ์การยอมรับฟีเจอร์, กรณีการใช้งาน, การตรวจสอบการถดถอย ไวยากรณ์ Gherkin ช่วยให้เขียนสถานการณ์เหล่านี้ได้โดยไม่ต้องมีความรู้ด้านการเขียนโปรแกรม
Arrange-Act-Assert เป็นตัวเลือกธรรมชาติสำหรับการทดสอบหน่วยที่ตรวจสอบเมธอดหรือคลาสเฉพาะ รูปแบบ AAA ไม่ต้องการเฟรมเวิร์กเพิ่มเติมและทำงานในภาษาโปรแกรมใดๆ สำหรับการพัฒนา iOS, Apple แนะนำ AAA ในเอกสาร XCTest (2024)
มาดูตัวอย่างเชิงปฏิบัติของ Given-When-Then ใน Kotlin สำหรับแอปพลิเคชัน Android ตัวอย่างแรกคือการทดสอบตะกร้าสินค้าโดยใช้ MockK ตัวอย่างที่สองคือการทดสอบตรรกะการแจ้งเตือนแบบพุช
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)
}
}
ตัวอย่างที่สองสาธิต Given-When-Then กับโค้ดแบบอะซิงโครนัส ที่นี่ Given กำหนดสถานะของ Firebase Cloud Messaging, When — การรับการแจ้งเตือนแบบพุช, Then — การตรวจสอบการประมวลผล
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)
}
}
ตัวอย่างที่สามคือสถานการณ์ BDD ใน Gherkin ที่แสดง Given-When-Then ในบริบทของการทดสอบการยอมรับ:
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
การนำ Given-When-Then ไปใช้อย่างมีประสิทธิภาพต้องปฏิบัติตามแนวทางปฏิบัติที่พิสูจน์แล้วหลายประการ แนวทางเหล่านี้รับประกันความสามารถในการอ่าน การบำรุงรักษา และการทำให้เป็นอัตโนมัติของสถานการณ์
กฎที่เข้มงวด: หนึ่งสถานการณ์ — หนึ่งการกระทำ หากจำเป็นต้องตรวจสอบลำดับของหลาย When ให้สร้างหลายสถานการณ์โดยที่ผลลัพธ์ของก่อนหน้ากลายเป็นเงื่อนไขเบื้องต้นของถัดไป สิ่งนี้ทำให้สถานการณ์เป็นอะตอมมิกและเข้าใจได้
Given ควรอธิบายสาระสำคัญ ไม่ใช่ตัวเลขเฉพาะเจาะจง แทนที่จะเป็น «Given ผู้ใช้ Ivanov มียอดคงเหลือ 500 รูเบิล» — «Given ผู้ใช้มียอดคงเหลือเพียงพอ» ข้อมูลเฉพาะเจาะจงจะถูกย้ายไปยัง Scenario Outline พร้อมตาราง Examples สิ่งนี้ทำให้สถานการณ์เป็นสากลและนำกลับมาใช้ใหม่ได้
การรวมสถานการณ์ Given-When-Then เข้ากับไปป์ไลน์การ intégration อย่างต่อเนื่องเปลี่ยนจากเอกสารเป็นการป้องกันการถดถอย คำขอรวมแต่ละครั้งในโปรเจกต์มือถือจะเรียกใช้สถานการณ์ BDD โดยอัตโนมัติและบล็อกการรวมหากมีสถานการณ์อย่างน้อยหนึ่งรายการล้มเหลว
สถานการณ์ BDD ด้วย Cucumber สำหรับ Android ถูกเรียกใช้ผ่าน งาน Gradle ./gradlew cucumber สำหรับ iOS (Quick/Nimble) — ผ่าน xcodebuild test ในระบบ CI (GitHub Actions, GitLab CI, Bitrise) การทดสอบ BDD จะถูกดำเนินการบนอีมูเลเตอร์หรืออุปกรณ์จริง รายงานถูกสร้างในรูปแบบ HTML ที่ผู้จัดการเข้าใจได้: สถานการณ์สีเขียว — ผ่าน, สีแดง — ล้มเหลวพร้อมระบุขั้นตอน
ไฟล์ .feature ถูกเก็บไว้ใน repository ข้างโค้ดและผ่าน การตรวจสอบโค้ด นักวิเคราะห์สร้างคำขอรวมกับสถานการณ์ใหม่ก่อนเริ่มการพัฒนา (BDD-first) นักพัฒนาเขียนนิยามขั้นตอนและการนำไปใช้เพื่อทำให้สถานการณ์เหล่านี้เป็นสีเขียว เมื่อสถานการณ์ทั้งหมดผ่าน — ฟังก์ชันการทำงานก็พร้อม แนวทางนี้ อธิบายไว้ในหนังสือของ Gojko Adzic «Specification by Example» (2011) เปลี่ยนความต้องการเป็นสิ่งประดิษฐ์ที่สามารถดำเนินการได้
คำถามที่พบบ่อย
ในเชิงโครงสร้าง ใช่ มันเป็น รูปแบบสามส่วนเดียวกัน ความแตกต่างอยู่ที่กลุ่มเป้าหมาย: Given-When-Then เน้นภาษาทางธุรกิจและใช้ใน BDD กับ Gherkin ในขณะที่ Arrange-Act-Assert เป็นรูปแบบทางเทคนิคสำหรับการทดสอบหน่วย การเลือกขึ้นอยู่กับบริบทและทีม
ไม่มีข้อจำกัด แต่แนะนำให้ ไม่เกิน 3–5 รายการ ต่อหนึ่ง Then หากมีการตรวจสอบมากกว่านั้น สถานการณ์อาจตรวจสอบมากเกินไปในการกระทำเดียว ให้แบ่งออกเป็นหลายสถานการณ์ด้วย Then ที่แตกต่างกัน
ไม่ รูปแบบนี้สามารถใช้ในเฟรมเวิร์กการทดสอบใดๆ ก็ได้ เพียงแบ่งการทดสอบด้วย ความคิดเห็น หรือบรรทัดว่างเป็นสามบล็อก Gherkin จำเป็นก็ต่อเมื่อสถานการณ์ถูกเขียนในรูปแบบไฟล์ .feature สำหรับ Cucumber หรือ SpecFlow
แนะนำให้แยกเงื่อนไขเบื้องต้นที่ซ้ำกันไปไว้ใน Background (Gherkin) หรือเมธอด @Before (JUnit) หากเงื่อนไขเบื้องต้นซับซ้อน ให้ใช้รูปแบบ Builder เพื่อสร้างข้อมูลทดสอบ สิ่งนี้ทำให้ Given สั้นและอ่านง่าย
ไม่ When เป็นบล็อกบังคับที่อธิบายการกระทำ หากสถานการณ์ตรวจสอบเฉพาะสถานะโดยไม่มีการกระทำ (เช่น «เมื่อโหลดแอปพลิเคชัน ข้อมูลควรถูกเก็บไว้ในแคช») When อธิบายตัวกระตุ้น: «เมื่อแอปพลิเคชันเริ่มทำงาน»
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ