การทำ Code Coverage ด้วย PHPUnit
หลังจากที่เราได้เรียนรู้การทำ Unit Testing ด้วย PHPUnit ไปแล้วในบทความก่อนหน้า อีกสิ่งหนึ่งที่สำคัญไม่แพ้กันคือการวัดคุณภาพของ Unit Tests ของเรา ซึ่งเราจะทำได้ด้วยการทำ Code Coverage นั่นเอง
Code Coverage คืออะไร?
Code Coverage คือการวัดว่า Unit Tests ของเราครอบคลุมโค้ดส่วนใดบ้างในโปรเจกต์ของเรา โดยจะแสดงผลออกมาเป็นเปอร์เซ็นต์ (เช่น 80% Code Coverage) ยิ่งเปอร์เซ็นต์สูง แสดงว่า Unit Tests ของเราครอบคลุมโค้ดได้มาก และมีโอกาสที่จะตรวจจับ Bug ได้มากขึ้น
สำคัญ: Code Coverage ไม่ได้หมายความว่าโค้ดของเราจะไม่มี Bug เลย แม้ว่า Code Coverage จะสูงถึง 100% ก็ตาม Code Coverage เป็นเพียงเครื่องมือที่ช่วยให้เรามั่นใจได้ว่า Unit Tests ของเราครอบคลุมโค้ดส่วนต่างๆ ได้อย่างทั่วถึง แต่คุณภาพของ Test Case และ Assertions ที่เราเขียนก็มีความสำคัญอย่างมากเช่นกัน
การสร้าง Code Coverage Report ด้วย PHPUnit
PHPUnit มีความสามารถในการสร้าง Code Coverage Report ได้อย่างง่ายดาย โดยเราจะต้องติดตั้ง Xdebug extension ก่อน หากยังไม่ได้ติดตั้ง ให้ติดตั้งโดยใช้คำสั่ง:
# สำหรับ Debian/Ubuntu
sudo apt-get install php-xdebug
# สำหรับ CentOS/RHEL
sudo yum install php-pecl-xdebug
# หลังจากติดตั้งแล้ว อย่าลืม restart web server (เช่น Apache หรือ Nginx)
sudo systemctl restart apache2
# หรือ
sudo systemctl restart nginx
ข้อผิดพลาดที่พบบ่อย: หากติดตั้ง Xdebug แล้ว PHPUnit ยังไม่สามารถสร้าง Code Coverage Report ได้ ให้ตรวจสอบว่า Xdebug ถูกเปิดใช้งานใน php.ini หรือไม่ โดยตรวจสอบไฟล์ php.ini และตรวจสอบว่ามีบรรทัด zend_extension=xdebug.so หรือไม่ หากไม่มี ให้เพิ่มบรรทัดนี้เข้าไป แล้ว restart web server
หลังจากติดตั้ง Xdebug แล้ว เราสามารถสร้าง Code Coverage Report ได้โดยใช้คำสั่ง:
./vendor/bin/phpunit --coverage-html coverage
คำสั่งนี้จะทำการรัน Unit Tests ทั้งหมด และสร้าง Code Coverage Report ในรูปแบบ HTML ในโฟลเดอร์ coverage ซึ่งเราสามารถเปิดไฟล์ index.html ในโฟลเดอร์นี้เพื่อดูรายงานได้
กรณีใช้งานจริง: สมมติว่าเรามี class ชื่อ Calculator ที่มี method add และ subtract ดังนี้:
// src/Calculator.php
class Calculator {
public function add(int $a, int $b): int {
return $a + $b;
}
public function subtract(int $a, int $b): int {
return $a - $b;
}
public function multiply(int $a, int $b): int {
return $a * $b;
}
}
และเรามี Unit Test สำหรับ method add และ subtract ดังนี้:
// tests/CalculatorTest.php
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase {
public function testAdd(): void {
$calculator = new Calculator();
$this->assertEquals(5, $calculator->add(2, 3));
}
public function testSubtract(): void {
$calculator = new Calculator();
$this->assertEquals(1, $calculator->subtract(3, 2));
}
}
เมื่อเรารันคำสั่ง ./vendor/bin/phpunit --coverage-html coverage เราจะได้ Code Coverage Report ที่แสดงให้เห็นว่า method multiply ไม่ได้ถูกครอบคลุมด้วย Unit Tests เลย
Analyzing Coverage
เมื่อเราเปิด Code Coverage Report เราจะเห็นรายการไฟล์ทั้งหมดในโปรเจกต์ของเรา พร้อมทั้งเปอร์เซ็นต์ Code Coverage สำหรับแต่ละไฟล์ นอกจากนี้เรายังสามารถคลิกที่ชื่อไฟล์เพื่อดูรายละเอียดเพิ่มเติมได้
ในหน้ารายละเอียด เราจะเห็นโค้ดของไฟล์นั้นๆ โดยจะมีสีต่างๆ แสดงถึงสถานะของแต่ละบรรทัด:
- สีเขียว: บรรทัดนี้ถูก execute โดย Unit Tests
- สีแดง: บรรทัดนี้ไม่ได้ถูก execute โดย Unit Tests
- สีส้ม: บรรทัดนี้ถูก execute บางส่วน แต่ไม่ทั้งหมด (เช่น มีเงื่อนไข if ที่ไม่ได้ถูกทดสอบทุกกรณี)
จากตัวอย่าง Calculator ด้านบน Code Coverage Report จะแสดงว่า method multiply เป็นสีแดง ซึ่งหมายความว่าเรายังไม่ได้เขียน Unit Test สำหรับ method นี้เลย
Improving Tests
เมื่อเราวิเคราะห์ Code Coverage Report แล้ว เราจะสามารถระบุได้ว่าโค้ดส่วนใดบ้างที่ยังขาด Unit Tests จากนั้นเราก็สามารถเขียน Unit Tests เพิ่มเติมเพื่อให้ครอบคลุมโค้ดส่วนนั้นๆ
จากตัวอย่าง Calculator ด้านบน เราจะเขียน Unit Test สำหรับ method multiply ดังนี้:
// tests/CalculatorTest.php
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase {
public function testAdd(): void {
$calculator = new Calculator();
$this->assertEquals(5, $calculator->add(2, 3));
}
public function testSubtract(): void {
$calculator = new Calculator();
$this->assertEquals(1, $calculator->subtract(3, 2));
}
public function testMultiply(): void {
$calculator = new Calculator();
$this->assertEquals(6, $calculator->multiply(2, 3));
}
}
หลังจากเพิ่ม Unit Test แล้ว เมื่อเรารันคำสั่ง ./vendor/bin/phpunit --coverage-html coverage อีกครั้ง Code Coverage Report จะแสดงให้เห็นว่า method multiply ถูกครอบคลุมด้วย Unit Tests แล้ว และเปอร์เซ็นต์ Code Coverage โดยรวมของไฟล์ Calculator.php ก็จะเพิ่มขึ้น
ข้อควรระวัง: การเพิ่ม Unit Tests เพื่อให้ Code Coverage สูงขึ้น ไม่ได้หมายความว่าโค้ดของเราจะดีขึ้นเสมอไป สิ่งสำคัญคือการเขียน Unit Tests ที่มีคุณภาพ สามารถตรวจสอบการทำงานของโค้ดได้อย่างถูกต้อง และครอบคลุมกรณีต่างๆ ที่เป็นไปได้
กรณีใช้งานจริง: ลองนึกภาพว่าเรามี function ที่ซับซ้อนที่มีเงื่อนไข if หลายชั้น การเขียน Unit Test ที่ครอบคลุมทุกเงื่อนไขอาจเป็นเรื่องยาก แต่การทำ Code Coverage จะช่วยให้เราเห็นว่าเงื่อนไขใดบ้างที่ยังไม่ได้ถูกทดสอบ และเราสามารถเขียน Unit Tests เพิ่มเติมเพื่อให้ครอบคลุมเงื่อนไขเหล่านั้น
สรุปแล้ว การทำ Code Coverage เป็นเครื่องมือที่มีประโยชน์ในการวัดคุณภาพของ Unit Tests ของเรา ช่วยให้เรามั่นใจได้ว่า Unit Tests ของเราครอบคลุมโค้ดส่วนต่างๆ ได้อย่างทั่วถึง และช่วยลดโอกาสที่จะเกิด Bug ในโปรเจกต์ของเรา