เทคนิคการออกแบบ Test Case ด้วย Black-Box และ White-Box Techniques

Jirawan.C
KBTG Life
Published in
5 min readApr 21, 2022

เมื่อมีฟังก์ชันหรือฟีเจอร์ใหม่ถูกเพิ่มเข้ามาในระบบ หลายคนมักจะเกิดคำถามเวลาทดสอบความถูกต้องในการทำงานของโค้ดที่เขียนขึ้น เช่น ต้องเขียน Unit Test เท่าไหร่ถึงจะเพียงพอ แล้วจะรู้ได้ยังไงว่า Test ที่เขียนไปจะครอบคลุมโค้ดใหม่ หรือโค้ดที่เราต้องการจะ Refactor

วันนี้จะขอมาแชร์วิธีการออกแบบ Unit Test ที่ใช้บ่อยๆ โดยจะนำเทคนิค Black Box มาใช้ในการหา Test Case ที่เป็น Minimum Test Case ไปจนถึงการเขียน Test Case ก่อนที่จะเริ่มลงมือเขียนโค้ดหรือ Refactor โค้ด ซึ่งหลายคนอาจจะเคยได้ยินหรือรู้จัก Specification Based หรือเทคนิค Black Box อยู่แล้ว หลังอ่านบทความนี้จบก็สามารถมาแบ่งปันวิธีการ Test ในรูปแบบที่ตัวเองปรับใช้หรือเทคนิคอื่นๆ กันได้นะคะ

Test Case Design คืออะไร

Test Case Design คือวิธีการเขียนหรือสร้าง Test เพื่อตรวจสอบหรือยืนยันว่าโค้ดที่จะถูกเขียนขึ้นมาทำงานถูกต้อง หรือหลังจากโค้ดถูก Refactor ไปแล้วยังสามารถทำงานได้ถูกต้อง

การเลือก Test Case ที่ดีจะช่วยให้เราประหยัดเวลาในการทำงาน ทั้งยังช่วยให้มั่นใจว่าโค้ดนั้นสามารถทำงานได้ถูกต้อง หรือถ้ามีบั๊กเราก็จะรู้ได้ทันที

Specification Based หรือ Black Block คืออะไร?

เราจะมองภาพทั้งระบบเป็นเหมือนกล่องดำ (Black Box) โดยไม่สนใจว่าการทำงานภายในเป็นอย่างไร เราจะดูแค่ว่าเมื่อใส่ Input เข้าไปในระบบแล้ว Output ที่ได้ตรงตาม Expected Result หรือไม่ ซึ่ง Black Box Testing มีด้วยกัน 4 เทคนิค ได้แก่

  1. Boundary Value Analysis
  2. Equivalence Partitioning
  3. Decision Table
  4. State Transition Testing

แล้ว White Box Testing คืออะไร?

การ Test ที่เราจะโฟกัสไปที่ภายในของระบบเพื่อดูว่าภายในฟังก์ชันนั้นมีการทำงานอะไรบ้าง จากนั้นออกแบบ Test Case ให้ครอบคลุมทั้งเงื่อนไขและ Input ต่างๆ โดย White Box Testing มีอยู่หลายเทคนิค คือ

  1. Statement Coverage
  2. Decision Coverage
  3. Condition Coverage
  4. Decision and Condition Coverage

ในบทความนี้ผู้เขียนจะหยิบมาสัก 2–3 เทคนิคที่ถูกนำมาใช้ในการทำงานบ่อยๆ พร้อมกับตัวอย่างโค้ดให้ดูกัน

Boundary Value Analysis

Boundary Value Analysis คือเทคนิคที่ใช้ข้อมูลที่อยู่ขอบๆ เป็น Input

  • Minimum Valid ข้อมูลที่อยู่ขอบล่างหรือค่าที่น้อยที่สุดที่ทำให้ระบบแสดงผลลัพธ์ถูกต้อง
  • Below the Minimum ข้อมูลที่อยู่ต่ำกว่าขอบล่าง 1 ตำแหน่งที่ทำให้ระบบแสดงผลลัพธ์ไม่ถูกต้อง
  • Above the Minimum ข้อมูลที่อยู่เหนือขอบล่างขึ้นมา 1 ตำแหน่งที่ทำให้ระบบแสดงผลลัพธ์ถูกต้อง
  • Maximum ข้อมูลที่อยู่ขอบบนที่ทำให้ระบบแสดงผลลัพธ์ถูกต้อง
  • Below the Maximum ข้อมูลที่อยู่ต่ำกว่าขอบบน 1 ตำแหน่ง ที่ทำให้ระบบแสดงผลลัพธ์ถูกต้อง
  • Above the Maximum ข้อมูลที่อยู่เหนือกว่าขอบบน 1 ตำแหน่งที่ทำให้ระบบแสดงผลลัพธ์ไม่ถูกต้อง

จาก 6 Tests นี้ เราจะเอาเทคนิค Boundary Value มาใช้ในการหา Input สำหรับแต่ละ Test Cases

Example 1: NameChecker

โปรแกรมนี้จะทำหน้าที่ตรวจสอบความยาวของชื่อที่รับเข้ามาว่าอยู่ระหว่าง 1-10 ตัวอักษรหรือไม่

  • Below the Minimum เท่ากับ 0
  • Minimum Value เท่ากับ 1
  • Above the Minimum เท่ากับ 2
  • Below Maximum เท่ากับ 9
  • Maximum Value เท่ากับ 10
  • Above Maximum value เท่ากับ 11

จากข้อมูลข้างต้น เราสามารถวาดเป็นตารางได้ดังนี้

จากตารางข้อมูลข้างต้นคือข้อมูลที่อยู่ตรงขอบและใกล้ๆ ขอบ ที่ทำให้ Expected Output เปลี่ยนไป เช่น ค่า 1 และ 10 คือค่าที่เป็นขอบๆ ของข้อมูล หรือชื่อที่มีความยาวตั้งแต่ 1 แต่ไม่เกิน 10 ตัวอักษร ส่วนค่า 0 หรือไม่กรอกตัวอักษรคือค่าแรกหรือค่าที่น้อยที่สุดที่เริ่มทำให้ข้อมูลไม่ถูกต้อง และค่า 11 หรือชื่อที่มีความยาวเกิน 10 ตัวอักษรจะเป็นข้อมูลที่ไม่ถูกต้อง

Equivalence Partitioning

เทคนิค Equivalence Partitioning คือการเลือกข้อมูลมาสัก 1 ค่าจากช่วงของข้อมูลที่อยู่ระหว่างขอบบนและขอบล่าง

จากตัวอย่างของ NameChecker เราสามารถเลือกค่ากลางหรือค่าใดก็ได้ตั้งแต่ 3 จนถึง 8 เทคนิคนี้จะช่วยให้เราเลือกค่าที่ใช้ในการทดสอบได้ง่ายขึ้นและครอบคลุมมากยิ่งขึ้น ประหยัดเวลาในการทำงานไปในตัว

Decision Table

Decision Table จะแสดงให้เห็นว่าแต่ละเงื่อนไขสามารถใส่ Input อะไรได้บ้างและจะแสดงผลอะไรบ้าง เทคนิคนี้ถูกนำมาใช้ในกรณีที่ต้องจัดการกับ Business Logic ที่ซับซ้อนหรือมีเงื่อนไขต้องตัดสินใจ เช่น If-else และ Switch Case

Example 1: NameChecker by Decision Table Technique

เราสามารถนำ Logic ต่างๆ มาวาดกราฟได้ดังรูปข้างต้น จะเห็นว่ามีทั้งหมด 2 เงื่อนไข เมื่อนำเงื่อนไขที่จะสามารถเกิดขึ้นได้มาทำในรูปแบบของตารางจะออกมาเป็นตามนี้

  1. ถ้าชื่อมีความยาวน้อยกว่า 1 ตัวอักษร ระบบจะแสดงผลลัพธ์ Invalid
  2. ถ้าชื่อมีความยาวเกิน 10 ตัวอักษร ระบบจะแสดงผลลัพธ์ Invalid
  3. ถ้าชื่อมีความยาวตั้งแต่ 1 ถึง 10 ตัวอักษร ระบบจะแสดงผลลัพธ์ Valid

Statement Coverage

เทคนิคนี้จะทำให้เรามั่นใจว่าโค้ดทุกบรรทัดจะต้องถูกรันอย่างน้อย 1 ครั้ง ถ้ามีบรรทัดใดไม่ถูกรัน ให้พิจารณาว่าบรรทัดนั้นยังจำเป็นอยู่หรือไม่ สามารถลบออกได้มั้ย หรือทำงานผิดปกติจากสาเหตุใด

มาเริ่มเขียนโค้ดจาก Test Case ที่เราสร้างไว้ด้านบนโดยใช้ Golang กัน

รัน Code Coverage โดยเลือก Package ที่ต้องการรัน Test แล้วคลิ้กขวา เลือกเมนู Go: Toggle Test Coverage In Current Package

ผลัพธ์จะแสดงให้เห็นว่า Test ที่เรารัน Coverage โค้ดบรรทัดไหน และ Coverage ไปกี่เปอร์เซ็นต์

จากรูปตัวอย่าง จะเห็นว่าบรรทัดที่ถูกรันอย่างน้อย 1 ครั้งจะถูกไฮไลท์ด้วยสีเขียว ช่วยให้เรามั่นใจได้ว่าทุกบรรทัดถูกรันแน่นอน วิธีนี้จะช่วยให้หาเซ็ตของ Test Case ที่น้อยที่สุดที่ครอบคลุมทุกเงื่อนไขและโค้ดทุกบรรทัด แต่ถ้าเกิดมีบรรทัดใดถูกไฮไลท์ด้วยสีแดง เราอาจจะต้องมาพิจารณากันอีกรอบว่า Test Case หรือโค้ดที่เราสร้างขึ้นมานั้นถูกต้องหรือไม่หรือมีสิ่งใดทำงานผิดพลาด

Example 2: Graders

ถัดไปจะเป็นตัวอย่างโปรแกรมคำนวณเกรดของนักเรียน โดยโปรแกรมจะรับคะแนน เป็นจำนวนเต็ม และแสดงผลลัพธ์เป็นเกรดต่างๆ ดังนี้

  • คะแนนตั้งแต่ 80 ขึ้นไป จะได้ เกรด A
  • คะแนนตั้งแต่ 70–79 จะได้เกรด B
  • คะแนนตั้งแต่ 60–69 จะได้เกรด C
  • คะแนนตั้งแต่ 50–59 จะได้เกรด D
  • คะแนนตั้งแต่ 0–49 จะได้เกรด F

ถ้ามีคะแนนที่เป็นค่าลบ ระบบจะไม่สามารถคำนวนเกรดได้และแสดงผลลัพธ์เป็น Invalid Score

จากข้อมูลด้านบน เราสามารถวาดกราฟการทำงานได้ดังนี้

ตอนนี้เรามีทั้งหมด 6 เงื่อนไขที่เป็นไปได้ ต่อไปเราจะมาหา Boundary Value กัน

Case: Grade A

Case: Grade B

Case: Grade C

Case: Grade D

Case: Grade F

Case: Invalid Score

หลังจากที่ได้ Test Case มาคร่าวๆ แล้ว ก็ได้เวลาเริ่มเขียน Unit Test และฟังก์ชัน Grader เราก็จะใช้ Golang เหมือนเดิม

เมื่อเขียนเสร็จเรียบร้อย ให้รัน Test Coverage ถ้าสังเกตพาเนลด้านซ้ายมือจะเห็นชื่อของ Test Case ที่ถูกรัน พาเนลตรงกลางและด้านล่าง จะแสดงผลลัพธ์ให้เห็นว่าบรรทัดใดถูกรันไปแล้วบ้าง และ Coverage กี่เปอร์เซ็นต์

อย่างไรก็ตามทั้งหมดนี้เป็นเพียงส่วนเล็กๆ ของการนำเทคนิค Black Box และ White Box มาหา Minimum Unit Test เทคนิคเหล่านี้อาจจะช่วยให้นักพัฒนาซอฟต์แวร์หา Test Case ได้ง่ายขึ้นหรือลดเวลาการเขียน Test ลดจำนวน Test ที่ซ้ำซ้อนหรือไม่ครอบคลุม แถมยังช่วยให้การ Refactor โค้ดในอนาคตทำได้ง่ายอีกด้วย

หวังว่าบทความนี้จะเป็นประโยชน์ต่อผู้พัฒนาที่อยากลองเริ่มเขียน Unit Testing แต่ไม่รู้จะเริ่มหา Test Case ยังไง หรือไม่แน่ใจว่า Unit Test ที่เขียนนั้นเพียงพอแล้วหรือยัง หากคุณชอบบทความนี้ รบกวนกด “Claps” 👏 เพื่อเป็นกำลังใจให้ผู้เขียนผลิตบทความต่อไปด้วยนะคะ ขอบคุณค่ะ

Source Code

References

สำหรับชาวเทคคนไหนที่สนใจเรื่องราวดีๆแบบนี้ หรืออยากเรียนรู้เกี่ยวกับ Product ใหม่ๆ ของ KBTG สามารถติดตามรายละเอียดกันได้ที่เว็บไซต์ www.kbtg.tech

--

--

Jirawan.C
KBTG Life

Hi, My name's JUGJIG. I working as a software engineer @Fintech company. I’m Go developer since 2020. I’m English learner. Nice to see you