การใช้ Data Providers: ทดสอบด้วยชุดข้อมูลที่หลากหลาย
ในบทที่แล้ว เราได้เรียนรู้เกี่ยวกับการใช้ Mocking เพื่อจำลอง Dependencies ภายนอก ซึ่งช่วยให้เราทดสอบโค้ดได้อย่างเป็นอิสระและควบคุมได้ ในบทนี้ เราจะมาเรียนรู้เกี่ยวกับอีกหนึ่งเครื่องมือที่มีประโยชน์อย่างมากในการทำ Unit Testing นั่นคือ Data Providers
Data Providers ช่วยให้เราสามารถทดสอบฟังก์ชันหรือเมธอดด้วยชุดข้อมูลที่หลากหลายได้อย่างง่ายดาย แทนที่จะต้องเขียน Test Case หลายๆ อันสำหรับแต่ละชุดข้อมูล เราสามารถกำหนดชุดข้อมูลทั้งหมดไว้ใน Data Provider และให้ PHPUnit ทำการรัน Test Case เดียวกันซ้ำๆ โดยใช้ข้อมูลแต่ละชุดจาก Data Provider
ทำไมต้องใช้ Data Providers?
- ลดความซ้ำซ้อนของโค้ด: แทนที่จะเขียน Test Case ที่คล้ายกันหลายๆ อัน เราสามารถรวมการทดสอบทั้งหมดไว้ใน Test Case เดียวโดยใช้ Data Provider
- ครอบคลุมกรณีทดสอบที่หลากหลาย: Data Providers ช่วยให้เราสามารถทดสอบฟังก์ชันด้วยข้อมูลที่แตกต่างกัน ซึ่งช่วยให้เรามั่นใจได้ว่าฟังก์ชันทำงานได้อย่างถูกต้องในทุกกรณี
- อ่านง่ายและบำรุงรักษาง่าย: Data Providers ทำให้โค้ดทดสอบของเราเป็นระเบียบและง่ายต่อการเข้าใจและแก้ไข
การสร้าง Data Provider Method
Data Provider คือเมธอดที่ส่งคืน Array ของ Array โดยแต่ละ Array ภายใน Array หลัก จะเป็นชุดข้อมูลที่ใช้ในการทดสอบ
รูปแบบของ Data Provider Method:
/**
* @return array
*/
public function dataProviderMethod()
{
return [
['value1', 'expectedResult1'],
['value2', 'expectedResult2'],
['value3', 'expectedResult3'],
];
}
อธิบาย:
- Data Provider Method ต้องเป็น `public`
- Data Provider Method ต้องมี `@return array` Annotation เพื่อบอก PHPUnit ว่าเมธอดนี้เป็น Data Provider
- Array ที่ส่งคืนต้องเป็น Array ของ Array โดยแต่ละ Array ย่อยคือชุดข้อมูลที่จะถูกส่งไปยัง Test Case
การใช้งาน Data Provider ใน Test
ในการใช้งาน Data Provider ใน Test Case เราจะต้องใช้ Annotation `@dataProvider` เพื่อระบุชื่อของ Data Provider Method ที่จะใช้
ตัวอย่าง:
use PHPUnit\Framework\TestCase;
class StringLengthTest extends TestCase
{
/**
* @dataProvider stringLengthProvider
*/
public function testStringLength(string $input, int $expectedLength)
{
$this->assertEquals($expectedLength, strlen($input));
}
/**
* @return array
*/
public function stringLengthProvider()
{
return [
['hello', 5],
['world', 5],
['', 0],
['a', 1],
];
}
}
อธิบาย:
- `@dataProvider stringLengthProvider`: Annotation นี้บอก PHPUnit ว่าจะใช้ `stringLengthProvider` เป็น Data Provider สำหรับ Test Case นี้
- `testStringLength(string $input, int $expectedLength)`: Test Case นี้รับ Parameter 2 ตัว คือ `$input` และ `$expectedLength` ซึ่งจะถูกส่งมาจาก Data Provider
- `stringLengthProvider()`: Data Provider Method นี้ส่งคืน Array ของ Array โดยแต่ละ Array ย่อยประกอบด้วย String (input) และ Integer (expectedLength)
เมื่อรัน Test Case นี้ PHPUnit จะทำการรัน `testStringLength` ทั้งหมด 4 ครั้ง โดยแต่ละครั้งจะใช้ข้อมูลจากแต่ละ Array ย่อยใน `stringLengthProvider`
ตัวอย่างเพิ่มเติม: การทดสอบฟังก์ชันคำนวณ
สมมติว่าเรามีฟังก์ชันที่ใช้ในการคำนวณพื้นที่สี่เหลี่ยม
function calculateRectangleArea(float $width, float $height): float
{
return $width * $height;
}
เราสามารถใช้ Data Provider เพื่อทดสอบฟังก์ชันนี้ด้วยความกว้างและความสูงที่แตกต่างกัน
use PHPUnit\Framework\TestCase;
class RectangleAreaTest extends TestCase
{
/**
* @dataProvider rectangleAreaProvider
*/
public function testCalculateRectangleArea(float $width, float $height, float $expectedArea)
{
$this->assertEquals($expectedArea, calculateRectangleArea($width, $height));
}
/**
* @return array
*/
public function rectangleAreaProvider()
{
return [
[2, 3, 6],
[5, 4, 20],
[1.5, 2.5, 3.75],
[0, 10, 0], // ทดสอบกรณีที่ความกว้างเป็น 0
];
}
}
ในตัวอย่างนี้ เราได้เพิ่มกรณีทดสอบที่ความกว้างเป็น 0 เพื่อให้แน่ใจว่าฟังก์ชันของเราจัดการกับกรณีพิเศษได้อย่างถูกต้อง
การทดสอบ Boundary Cases
Boundary Cases คือค่าที่อยู่บริเวณขอบเขตของ Input ที่เป็นไปได้ การทดสอบ Boundary Cases เป็นสิ่งสำคัญเพื่อให้แน่ใจว่าฟังก์ชันของเราทำงานได้อย่างถูกต้องเมื่อได้รับ Input ที่ "สุดโต่ง" เช่น ค่าที่น้อยที่สุด, ค่าที่มากที่สุด, หรือค่าที่ใกล้เคียงกับ 0
ตัวอย่างเช่น ถ้าเรามีฟังก์ชันที่รับ Integer เป็น Input และ Input นั้นต้องอยู่ในช่วง 1-100 เราควรทดสอบ Boundary Cases เช่น 1, 100, 0, และ 101 เพื่อให้แน่ใจว่าฟังก์ชันของเราทำงานได้อย่างถูกต้อง
ในการใช้ Data Providers เพื่อทดสอบ Boundary Cases เราสามารถเพิ่ม Boundary Cases เหล่านั้นเข้าไปใน Data Provider ของเราได้เลย
use PHPUnit\Framework\TestCase;
class NumberInRangeTest extends TestCase
{
/**
* @dataProvider numberInRangeProvider
*/
public function testNumberInRange(int $input, bool $expectedResult)
{
$this->assertEquals($expectedResult, $this->isNumberInRange($input));
}
/**
* @return array
*/
public function numberInRangeProvider()
{
return [
[1, true], // Boundary Case: ค่าต่ำสุด
[100, true], // Boundary Case: ค่าสูงสุด
[50, true], // ค่าปกติ
[0, false], // Boundary Case: ต่ำกว่าค่าต่ำสุด
[101, false], // Boundary Case: สูงกว่าค่าสูงสุด
];
}
private function isNumberInRange(int $number): bool
{
return $number >= 1 && $number <= 100;
}
}
ในตัวอย่างนี้ เราได้ทดสอบ Boundary Cases ทั้งหมด (1, 100, 0, และ 101) เพื่อให้แน่ใจว่าฟังก์ชัน `isNumberInRange` ของเราทำงานได้อย่างถูกต้อง
สรุป
Data Providers เป็นเครื่องมือที่มีประโยชน์อย่างมากในการทำ Unit Testing ช่วยให้เราสามารถทดสอบฟังก์ชันด้วยชุดข้อมูลที่หลากหลายได้อย่างง่ายดาย ลดความซ้ำซ้อนของโค้ด และเพิ่มความครอบคลุมของการทดสอบ นอกจากนี้ Data Providers ยังช่วยให้โค้ดทดสอบของเราเป็นระเบียบและง่ายต่อการเข้าใจและแก้ไข
ในบทถัดไป เราจะมาเรียนรู้เกี่ยวกับ Code Coverage ซึ่งเป็นเครื่องมือที่ช่วยให้เราวัดคุณภาพของการทดสอบของเราได้