ข้าม​ไป​ยัง​เนื้อหา

Behavior Driven Development

นิยาม​พฤติกรรม​ของ​ระบบ​ด้วย​ภาษา​ที่​ทุก​ฝ่าย​เข้าใจ​ตรง​กัน

Behavior Driven Development (BDD) เป็น​แนว​ปฏิบัติ​แบบ Agile ที่​เน้น​การ​สร้าง ความ​เข้าใจ​ร่วม​กัน (shared understanding) ระหว่าง​ฝ่าย​ธุรกิจ​กับ​ฝ่าย​เทคนิค โดย​อธิบาย​พฤติกรรม​ของ​ระบบ​ด้วย​ภาษา​ธรรมดา​ที่​ทุก​คน​อ่าน​ออก แทนที่​จะ​เป็น spec ทาง​เทคนิค​หรือ code ล้วน ๆ มัน​เป็น whole team activity ที่​ดึง​นัก​พัฒนา ผู้​ทดสอบ (QA) และ​ตัวแทน​ฝ่าย​ธุรกิจ​มา​คุย​กัน​ตั้งแต่​ก่อน​เขียน code บรรทัด​แรก

BDD ถือ​กำเนิด​ขึ้น​ราว​ปี 2003 โดย Dan North ขณะ​ทำงาน​ที่ ThoughtWorks เขา​สังเกต​ว่า​เวลา​สอน Test Driven Development ให้​ทีม​ต่าง ๆ ปัญหา​ซ้ำ ๆ ที่​เจอ​คือ​ความ​สับสน​รอบ​คำ​ว่า “test” — โปรแกรมเมอร์​ไม่​แน่ใจ​ว่า​จะ​เริ่ม test จาก​ตรง​ไหน ควร test อะไร​บ้าง ควร test มาก​แค่​ไหน​ใน​แต่ละ​รอบ ควร​ตั้ง​ชื่อ test อย่างไร และ​เมื่อ test ล้มเหลว​ควร​ตีความ​ว่า​อย่างไร North จึง​เสนอ​ให้​เปลี่ยน​คำ​ศัพท์​จาก “test” เป็น “behavior” — เขียน spec ใน​รูป​ประโยค​ที่​ว่า class หนึ่ง ๆ should ทำ​อะไร ซึ่ง​ช่วย​ให้​โฟกัส​อยู่​ที่​พฤติกรรม​ของ​หน่วย code นั้น​โดยตรง เขา​เริ่ม​เขียน framework JBehave แทนที่ JUnit เพื่อ​ลบ​ศัพท์​แสง​เรื่อง testing ออก​ไป​ทั้งหมด แล้ว​แทนที่​ด้วย​คำ​ศัพท์​ที่​พูด​ถึง​พฤติกรรม​ล้วน ๆ

ข้อ​เข้าใจ​ผิด​ที่​พบ​บ่อย​คือ​คิด​ว่า BDD เป็น​แค่ “TDD version ปรับปรุง” แต่​จริง ๆ แล้ว BDD ผสาน TDD เข้า​กับ​แนวคิด​จาก Domain-Driven Design และ acceptance testing/ATDD โดย​ดึง Ubiquitous Language มา​ใช้​เป็น​แกน​กลาง — คำ​ศัพท์​ที่​ใช้​ใน​เอกสาร requirement, ใน​บทสนทนา​กับ stakeholder และ​ใน code ควร​เป็น​คำ​ชุด​เดียวกัน เพื่อ​ไม่​ต้อง “แปล” ไป​มาระหว่าง​ภาษา​ธุรกิจ​กับ​ภาษา​ทาง​เทคนิค เมื่อ scenario ที่​เขียน​ไว้​ล้มเหลว นั่น​คือ​สัญญาณ​ตรง ๆ ว่า​พฤติกรรม​จริง​ของ​ระบบ​ยัง​ไม่​ตรง​กับ​ที่​ตกลง​กัน​ไว้ ไม่ใช่​แค่ “test พัง”

องค์​ประกอบ​พื้นฐาน​ของ BDD คือ User Stories, Scenarios, Steps และ Automation โดย​ใช้​ภาษา​ที่​มี​จังหวะ (cadence) ตายตัว ภาษา​เฉพาะ​ทาง (domain-specific language) ที่​นิยม​ใช้​เขียน scenario คือ Gherkin

กระบวนการ​ทำงาน​ทั่วไป​แบ่ง​เป็น​สาม​ช่วง ได้แก่ discovery ที่​ทีม​คุย​กัน​เพื่อ​หาความ​เข้าใจ​ร่วม (มัก​เรียก​ว่า Three Amigos — รวม​นัก​พัฒนา ผู้​ทดสอบ และ​ตัวแทน​ฝ่าย​ธุรกิจ), formulation ที่​แปลง​บทสนทนา​เป็น scenario ด้วย Gherkin และ automation ที่​ผูก scenario เข้า​กับ code จริง​ให้​รัน​อัตโนมัติ​ได้ ผลลัพธ์​ที่​ได้​คือ Gherkin feature file ที่​ทำ​หน้าที่​เป็น​ทั้ง requirement, test และ living documentation ใน file เดียว

flowchart LR
  Discovery[Three Amigos พูดคุยหาความเข้าใจร่วม] --> Formulation[เขียน Scenario ด้วย Gherkin]
  Formulation --> Automation[ผูก Step กับ code]
  Automation --> Execution[รัน Suite อัตโนมัติ]
  Execution --> LivingDoc[Living Documentation]
  LivingDoc --> Discovery

BDD มัก​เริ่ม​จาก user story ซึ่ง​เป็น​คำ​บรรยาย feature ระดับ​สูง​จาก​มุมมอง​ของ​ผู้​ใช้​ปลายทาง เขียน​ด้วย​ภาษา​ธรรมดา​ให้​สมาชิก​ที่​ไม่ใช่​สาย​เทคนิค​เข้าใจ​ได้ โดย​มัก​มี​จังหวะ​ดังนี้:

โครง​ของ user story — เน้น​ว่า​ใคร ต้องการ​อะไร และ​เพื่อ​อะไร

# As a [ผู้ใช้/บทบาท], I want to [ทำบางสิ่ง], So that [เหตุผล]
Feature: การยืมหนังสือ
ในฐานะสมาชิกห้องสมุด
ฉันต้องการยืม eBook
เพื่อจะได้อ่าน eBook บนเครื่องอ่านของฉัน

แต่ละ user story ถูก​แตก​ออก​เป็น​หลาย scenario ที่​บรรยาย​กรณี​ใช้งาน​เฉพาะ​ของ feature โดย​เขียน​ใน​จังหวะ​ที่​เรียก​ว่า Given-When-Then ซึ่ง Martin Fowler อธิบาย​ไว้​ว่า​เป็น​วิธี​จัด​โครงสร้าง​ของ SpecificationByExample: Given อธิบาย​เงื่อนไข​ตั้งต้น​ก่อน​พฤติกรรม​จะ​เกิด, When อธิบาย​การกระทำ​หรือ​เหตุการณ์​ที่​จุด​ชนวน​พฤติกรรม​นั้น, และ Then อธิบาย​ผลลัพธ์​ที่​คาด​หวัง​หลัง​พฤติกรรม​เกิด​ขึ้น การ​เขียน​ใน​รูปแบบ​นี้​ทำให้​ชัดเจน​ว่า​พฤติกรรม​ของ​ระบบ​ควร​เป็น​อย่างไร​ภาย​ใต้​เงื่อนไข​หนึ่ง ๆ

scenario ของ​ระบบ​ยืม​หนังสือ​ออนไลน์

Feature: การยืมออนไลน์
Scenario: ยืมหนังสือหนึ่งเล่ม
Given คลังของห้องสมุดมีหนังสือที่ฉันต้องการยืม
When ฉันยืมหนังสือเล่มนั้น
Then จำนวนคงเหลือในคลังลดลง 1 เล่ม

จุด​เด่น​สำคัญ​ข้อ​หนึ่ง​ของ BDD คือ​การนำ​ไป​ใช้​ใน​การ​ทดสอบ ด้วย​การ​ใช้ library เชื่อม Gherkin เข้า​กับ code test ที่​ขับ​ด้วย​พฤติกรรม​จึง​ถูก​ทำให้​อัตโนมัติ​ได้ Gherkin ยัง​รองรับ​ตัวแปร​ใน​แต่ละ​ขั้น​ของ scenario ผ่าน feature ที่​เรียก​ว่า Scenario Outline:

Scenario Outline — รัน​ชุด​ตัวอย่าง​หลาย​กรณี​จาก​ตาราง​เดียว

Scenario Outline: ตรวจวันหมดอายุการยืม
Given ระยะเวลายืม <period> วัน
When ฉันเปิดหนังสือในวันที่ <open>
Then หนังสือควรหมดอายุในอีก <left> วัน
Examples:
| period | open | left |
| 7 | 2 | 5 |
| 14 | 10 | 2 |
| 21 | 18 | 3 |

มี library จำนวน​มาก​ที่​ช่วย map file Gherkin เข้า​กับ code เพื่อ​ทำ test อัตโนมัติ ที่​พบ​บ่อย​ได้แก่ Cucumber-JVM/JBehave (Java), SpecFlow (C#/.NET), behave/pytest-bdd (Python) และ Cucumber (Ruby)

ตัวอย่าง​การ​ผูก scenario ข้าง​ต้น​เข้า​กับ step definition ใน C# ด้วย SpecFlow ซึ่ง​เป็น framework BDD ที่​นิยม​ที่สุด​ใน​ระบบ​นิเวศ .NET — SpecFlow แปล file .feature เป็น Gherkin แล้ว generate เมทอด​ที่​ต้อง implement ให้​ตรง​กับ​แต่ละ step:

[Binding]
public class LibraryLoanSteps
{
private readonly LibraryCatalog _catalog;
private int _remainingBeforeLoan;
public LibraryLoanSteps(LibraryCatalog catalog)
{
_catalog = catalog;
}
[Given(@"คลังของห้องสมุดมีหนังสือที่ฉันต้องการยืม")]
public void GivenBookIsAvailable()
{
_catalog.AddCopy(new BookId("book-1"));
_remainingBeforeLoan = _catalog.RemainingCopies(new BookId("book-1"));
}
[When(@"ฉันยืมหนังสือเล่มนั้น")]
public void WhenIBorrowTheBook()
{
_catalog.Borrow(new BookId("book-1"), new MemberId("member-1"));
}
[Then(@"จำนวนคงเหลือในคลังลดลง 1 เล่ม")]
public void ThenRemainingCopiesDecreaseByOne()
{
var remaining = _catalog.RemainingCopies(new BookId("book-1"));
Assert.Equal(_remainingBeforeLoan - 1, remaining);
}
}

โครงสร้าง​นี้​ทำให้ file .feature (ที่ product owner อ่าน​ได้) กับ code step definition (ที่​นัก​พัฒนา​ดูแล) แยก​จาก​กัน​ชัดเจน แต่​ยัง​คง sync กัน​อยู่​เสมอ เพราะ SpecFlow จะ fail การ build ทันที​ถ้า​มี step ใน file Gherkin ที่​ยัง​ไม่มี step definition รองรับ

ประโยชน์

  • ร่วมมือ​และ​ได้​ฟีด​แบ็ก​ต่อ​เนื่อง — BDD ส่งเสริม​ให้​นัก​พัฒนา ผู้​ทดสอบ และ​ฝ่าย​ธุรกิจ​ร่วม​กัน​นิยาม​และ​ขัดเกลา scenario จน​ทุก​คน​เข้าใจ​ตรง​กัน​ว่า​ซอฟต์แวร์​ควร​ทำ​อะไร
  • Living documentation — feature file ที่​เขียน​ด้วย Gherkin ทำ​หน้าที่​เป็น​ทั้ง spec และ​ชุด​ทดสอบ​ใน​ตัว​เดียว เมื่อ code เปลี่ยน scenario ที่​ผูก​ไว้​จะ​ล้มเหลว​ทันที​ถ้า​พฤติกรรม​ไม่​ตรง​กับ​ที่​ตกลง​กัน จึง​จับ​ปัญหา​ความ​เข้าใจ​ผิด​ได้​เร็ว​กว่า​รอ​ถึง​รอบ QA
  • ลด​ความ​กำกวม​ของ​คำ​ว่า “test” — การ​พูด​ถึง “พฤติกรรม” แทน “การ​ทดสอบ” ช่วย​ให้​ทีม​โฟกัส​ที่​ผลลัพธ์​เชิง​ธุรกิจ ไม่ใช่​แค่ coverage ตัวเลข

ข้อ​ควร​ระวัง

  • Scenario ระเบิด​จำนวน (scenario explosion) — ถ้า​ไม่​ระวัง​การ​ออกแบบ scenario จะ​เพิ่ม​จำนวน​แบบ​ทวีคูณ​ตาม​เงื่อนไข ทำให้ suite รัน​ช้า​และ​ดูแล​ยาก ควร​เลือก scenario ที่​สื่อ​ความหมาย​ทาง​ธุรกิจ​จริง ๆ ไม่ใช่​ไล่​ทุก combination
  • ใช้ Gherkin เป็น​แค่ wrapper ของ UI automation — ถ้า​เขียน step ผูก​กับ​ปุ่ม​และ selector บน​หน้า​จอ​โดยตรง scenario จะ​เปราะบาง​และ​หลุด​จาก​ภาษา​ธุรกิจ ทำให้​เสีย​จุด​ประสงค์​หลัก​ของ BDD ไป
  • ข้าม Three Amigos — ถ้า​ให้​นัก​พัฒนา​คน​เดียว​เขียน​ทั้ง feature file และ step definition โดย​ไม่​คุย​กับ​ฝ่าย​ธุรกิจ BDD จะ​เหลือ​แค่ framework test ที่​หน้าตา​แปลก​กว่า​เดิม ไม่​ได้​ความ​เข้าใจ​ร่วม​กัน​ตาม​เจตนารมณ์​เดิม
  • ต้นทุน​การ​ดูแล step definitions — ทุก step ต้อง​มี code รองรับ และ​เมื่อ scenario จำนวน​มาก​ขึ้น การ refactor ให้ step ใช้​ซ้ำ​ได้​จึง​สำคัญ​ไม่​แพ้​การ refactor code production