การทดสอบ Code OOP: Unit Testing
เมื่อเราสร้างโค้ด OOP ที่ซับซ้อน การตรวจสอบให้แน่ใจว่าโค้ดทำงานได้อย่างถูกต้องเป็นสิ่งสำคัญอย่างยิ่ง วิธีการหนึ่งที่ได้รับความนิยมและมีประสิทธิภาพคือการใช้ Unit Testing ในบทความนี้ เราจะมาทำความเข้าใจว่า Unit Testing คืออะไร ทำไมถึงสำคัญ และเราจะนำไปใช้กับโค้ด OOP ของเราได้อย่างไร
Unit Testing คืออะไร?
Unit Testing คือการทดสอบส่วนย่อยที่สุดของโค้ดของเรา หรือ "unit" ซึ่งโดยทั่วไปคือฟังก์ชันหรือเมธอดใน Class โดยมีจุดมุ่งหมายเพื่อตรวจสอบว่าแต่ละ unit ทำงานตามที่คาดหวังไว้หรือไม่ การทดสอบเหล่านี้มักจะถูกเขียนขึ้นโดยนักพัฒนาเพื่อตรวจสอบความถูกต้องของโค้ดที่ตนเองเขียนขึ้น
เหตุผลเชิงออกแบบ: การทำ Unit Testing ช่วยให้เราสามารถตรวจสอบความถูกต้องของโค้ดได้ตั้งแต่เนิ่นๆ หากพบข้อผิดพลาด เราจะสามารถแก้ไขได้ง่ายกว่าการรอจนถึงขั้นตอนการทดสอบที่ใหญ่ขึ้น (เช่น Integration Testing หรือ End-to-End Testing) ซึ่งอาจทำให้การระบุสาเหตุของปัญหาทำได้ยากกว่า
ทำไมต้อง Unit Testing?
- ลดข้อผิดพลาด: ช่วยให้เราค้นพบข้อผิดพลาดในโค้ดได้ตั้งแต่เนิ่นๆ
- ปรับปรุงคุณภาพโค้ด: บังคับให้เราคิดอย่างรอบคอบเกี่ยวกับโค้ดที่เราเขียน และทำให้โค้ดมีความ module มากขึ้น
- รองรับการ Refactoring: เมื่อเราต้องการปรับปรุงโค้ด (Refactoring) Unit Tests จะช่วยให้เรามั่นใจได้ว่าการเปลี่ยนแปลงของเราจะไม่ทำให้เกิดข้อผิดพลาดใหม่
- Documentation: Unit Tests สามารถใช้เป็นเอกสารประกอบโค้ดได้ เนื่องจากแสดงให้เห็นว่าโค้ดควรทำงานอย่างไร
- Code Coverage: ช่วยให้เราทราบว่าโค้ดส่วนใดบ้างที่ได้รับการทดสอบ และส่วนใดที่ยังไม่ได้ทดสอบ
การเขียน Unit Tests
การเขียน Unit Tests โดยทั่วไปจะประกอบด้วยขั้นตอนหลักๆ ดังนี้:
- Arrange: เตรียมข้อมูลและสถานะเริ่มต้นที่จำเป็นสำหรับการทดสอบ
- Act: เรียกใช้ฟังก์ชันหรือเมธอดที่เราต้องการทดสอบ
- Assert: ตรวจสอบว่าผลลัพธ์ที่ได้เป็นไปตามที่เราคาดหวังไว้หรือไม่
ตัวอย่าง: สมมติว่าเรามี Class ชื่อ `Calculator` ที่มีเมธอด `add` สำหรับการบวกเลขสองจำนวน:
class Calculator {
public function add(int $a, int $b): int {
return $a + $b;
}
}
เราสามารถเขียน Unit Test สำหรับเมธอด `add` ได้ดังนี้ (โดยใช้ PHPUnit ซึ่งเป็นเครื่องมือ Unit Testing ที่ได้รับความนิยม):
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase {
public function testAdd_PositiveNumbers_ReturnsSum(): void {
// Arrange
$calculator = new Calculator();
$a = 5;
$b = 3;
// Act
$result = $calculator->add($a, $b);
// Assert
$this->assertEquals(8, $result);
}
public function testAdd_NegativeNumbers_ReturnsSum(): void {
// Arrange
$calculator = new Calculator();
$a = -5;
$b = -3;
// Act
$result = $calculator->add($a, $b);
// Assert
$this->assertEquals(-8, $result);
}
public function testAdd_MixedNumbers_ReturnsSum(): void {
// Arrange
$calculator = new Calculator();
$a = 5;
$b = -3;
// Act
$result = $calculator->add($a, $b);
// Assert
$this->assertEquals(2, $result);
}
}
ในตัวอย่างนี้ เราได้สร้าง Class ชื่อ `CalculatorTest` ซึ่งสืบทอดมาจาก `PHPUnit\Framework\TestCase` และมีเมธอดทดสอบ 3 เมธอด:
- `testAdd_PositiveNumbers_ReturnsSum`: ทดสอบกรณีที่เลขทั้งสองเป็นบวก
- `testAdd_NegativeNumbers_ReturnsSum`: ทดสอบกรณีที่เลขทั้งสองเป็นลบ
- `testAdd_MixedNumbers_ReturnsSum`: ทดสอบกรณีที่เลขหนึ่งเป็นบวกและอีกเลขหนึ่งเป็นลบ
แต่ละเมธอดทดสอบจะทำตามขั้นตอน Arrange, Act, Assert โดย:
- Arrange: สร้าง instance ของ `Calculator` และกำหนดค่าของ `$a` และ `$b`
- Act: เรียกใช้เมธอด `add` และเก็บผลลัพธ์ไว้ใน `$result`
- Assert: ใช้ `$this->assertEquals` เพื่อตรวจสอบว่า `$result` มีค่าเท่ากับที่เราคาดหวังไว้หรือไม่
แนวปฏิบัติที่ดี:
- เขียน Tests ก่อนเขียน Code (Test-Driven Development - TDD): แนวทางนี้จะช่วยให้เราคิดอย่างรอบคอบเกี่ยวกับสิ่งที่โค้ดของเราควรทำ ก่อนที่จะเริ่มเขียนโค้ดจริงๆ
- ตั้งชื่อ Tests ให้สื่อความหมาย: ชื่อ Test ควรบอกได้อย่างชัดเจนว่า Test นั้นกำลังทดสอบอะไร
- Keep Tests Simple: Test แต่ละตัวควรทดสอบเพียงสิ่งเดียว
- Write Independent Tests: Test แต่ละตัวไม่ควรขึ้นอยู่กับ Test ตัวอื่นๆ
เครื่องมือที่ใช้ในการ Unit Testing
PHP มีเครื่องมือ Unit Testing หลายตัวให้เลือกใช้ แต่ที่ได้รับความนิยมมากที่สุดคือ:
- PHPUnit: เป็น framework สำหรับ Unit Testing ที่ได้รับความนิยมและใช้งานกันอย่างแพร่หลาย มี features มากมายและรองรับการเขียน Test ที่ซับซ้อนได้
การติดตั้ง PHPUnit: สามารถติดตั้ง PHPUnit ได้ง่ายๆ โดยใช้ Composer:
composer require --dev phpunit/phpunit
การรัน Unit Tests: หลังจากติดตั้ง PHPUnit แล้ว เราสามารถรัน Test ได้โดยใช้คำสั่ง:
./vendor/bin/phpunit
Trade-offs: การเขียน Unit Tests ต้องใช้เวลาและความพยายาม แต่ผลตอบแทนที่ได้คือโค้ดที่มีคุณภาพสูงขึ้น ลดข้อผิดพลาด และง่ายต่อการบำรุงรักษาในระยะยาว การตัดสินใจว่าจะเขียน Unit Tests สำหรับส่วนใดของโค้ด ขึ้นอยู่กับความซับซ้อนและความสำคัญของโค้ดส่วนนั้นๆ
สรุป
Unit Testing เป็นเครื่องมือที่สำคัญสำหรับการพัฒนาซอฟต์แวร์ OOP ช่วยให้เรามั่นใจได้ว่าโค้ดของเราทำงานได้อย่างถูกต้อง และลดความเสี่ยงที่จะเกิดข้อผิดพลาด การเรียนรู้และนำ Unit Testing ไปใช้ จะช่วยให้เราเป็นนักพัฒนาที่เก่งขึ้น และสร้างซอฟต์แวร์ที่มีคุณภาพสูงขึ้น