เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ
ขับเคลื่อนการออกแบบด้วยวงจรแดง-เขียว-ปรับโครงสร้าง
เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ เป็นบทเรียน PHP Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน PHP Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
การพัฒนาที่ขับเคลื่อนด้วยการทดสอบเป็นเครื่องมือออกแบบ
การพัฒนาที่ขับเคลื่อนด้วยการทดสอบมักถูกนำเสนอว่าเป็นเทคนิคการทดสอบ แต่คุณค่าที่แท้จริงคือแรงกดดันด้านการออกแบบ การเขียนการทดสอบก่อนบังคับให้คุณกำหนดส่วนติดต่อสาธารณะของวัตถุ สิ่งที่วัตถุต้องพึ่งพา และสัญญาของวัตถุก่อนจะเขียนการนำไปใช้ใด ๆ การทดสอบเป็นเพียงผลพลอยได้ ส่วนการออกแบบที่ดีขึ้นคือผลลัพธ์
แดง เขียว ปรับโครงสร้าง
วงจรนี้มีสามระยะที่เคร่งครัด:
- แดง — เขียนการทดสอบที่ไม่ผ่านสำหรับพฤติกรรมเล็ก ๆ ถัดไป การทดสอบต้องไม่ผ่านด้วยเหตุผลที่ถูกต้อง
- เขียว — เขียนโค้ดเท่าที่จำเป็นเพื่อให้ผ่าน แม้โค้ดจะดูไม่สวยก็ตาม
- ปรับโครงสร้าง — จัดระเบียบการนำไปใช้และการทดสอบขณะยังคงผ่านอยู่
วินัยเป็นสิ่งสำคัญ: อย่าข้ามขั้นแดง (มิฉะนั้นคุณอาจกำลังทดสอบสิ่งที่ไม่มีอยู่จริง) และอย่าสร้างเกินจำเป็นในขั้นเขียว
แดง: เขียนการทดสอบที่ไม่ผ่านก่อน
เราจะสร้าง PriceCalculator ที่ใช้ส่วนลดเป็นเปอร์เซ็นต์ เริ่มด้วยการทดสอบพฤติกรรมที่ยังไม่มีอยู่ การเรียกใช้ควรไม่ผ่านเพราะยังไม่ได้ประกาศคลาส — นั่นคือขั้นแดงที่ถูกต้อง
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorTest extends TestCase
{
public function test_applies_a_percentage_discount(): void
{
$calc = new PriceCalculator();
// 100 with 10% off -> 90.0
self::assertSame(90.0, $calc->withDiscount(100.0, 10));
}
}
เขียว: เขียนโค้ดเท่าที่จำเป็นเพื่อให้ผ่าน
ตอนนี้ให้เขียนการนำไปใช้ที่เล็กที่สุดเพื่อเปลี่ยนผลลัพธ์ให้เป็นสีเขียว อย่าเพิ่งเพิ่มกฎการปัดเศษ การตรวจสอบความถูกต้อง หรือการจัดการสกุลเงิน — ยังไม่มีการทดสอบที่ไม่ผ่านใดเรียกร้องสิ่งเหล่านี้
<?php
final class PriceCalculator
{
public function withDiscount(float $amount, float $percent): float
{
return $amount - ($amount * $percent / 100);
}
}
$calc = new PriceCalculator();
var_dump($calc->withDiscount(100.0, 10)); // float(90)
ใช้หลายกรณีเพื่อผลักดันความครอบคลุม
การทดสอบเดียวอาจผ่านได้ด้วยการฝังค่าไว้ตายตัว การตรวจสอบด้วยหลายกรณี — การเพิ่มตัวอย่างที่แตกต่างกันอีกหนึ่งกรณี — จะบังคับให้การนำไปใช้ครอบคลุมทั่วไปมากขึ้น เพิ่มกรณีที่วิธีแก้แบบง่าย ๆ ไม่สามารถแสร้งทำให้ผ่านได้ เพื่อผลักดันให้เกิดสูตรที่ถูกต้อง
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorTest extends TestCase
{
public function test_zero_percent_is_unchanged(): void
{
self::assertSame(50.0, (new PriceCalculator())->withDiscount(50.0, 0));
}
public function test_full_discount_is_free(): void
{
self::assertSame(0.0, (new PriceCalculator())->withDiscount(50.0, 100));
}
}
ใช้กรณีขอบเป็นข้อกำหนด
ในการพัฒนาที่ขับเคลื่อนด้วยการทดสอบ ข้อกำหนดใหม่คือการทดสอบใหม่ที่ไม่ผ่าน สมมติว่าส่วนลดติดลบไม่ถูกต้อง ให้เขียนการทดสอบที่คาดว่าจะเกิดข้อยกเว้นก่อน การทดสอบจะไม่ผ่านเพราะยังไม่มีสิ่งใดโยนข้อยกเว้น การทดสอบนี้บันทึกสัญญาไว้
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorValidationTest extends TestCase
{
public function test_rejects_negative_discount(): void
{
$this->expectException(\InvalidArgumentException::class);
(new PriceCalculator())->withDiscount(100.0, -5);
}
}
เขียวอีกครั้ง แล้วจึงปรับโครงสร้าง
เพิ่มเงื่อนไขป้องกันเพื่อให้การทดสอบใหม่ผ่าน จากนั้นจึงปรับโครงสร้างได้อย่างมั่นใจ — การทดสอบที่มีอยู่จะตรวจจับการถดถอย สังเกตว่าเราเพิ่มการปัดเศษก็ต่อเมื่อมีการทดสอบ (หรือข้อกำหนดที่ทราบแน่ชัด) รองรับแล้วเท่านั้น
<?php
final class PriceCalculator
{
public function withDiscount(float $amount, float $percent): float
{
if ($percent < 0 || $percent > 100) {
throw new \InvalidArgumentException('percent must be 0..100');
}
return round($amount - ($amount * $percent / 100), 2);
}
}
var_dump((new PriceCalculator())->withDiscount(19.99, 15)); // float(16.99)
ผู้จัดหาข้อมูลช่วยย่อตัวอย่าง
เมื่อพฤติกรรมใดคงที่แล้ว ให้รวมคู่ตัวอย่างจำนวนมากเป็นการทดสอบแบบมีพารามิเตอร์เพียงรายการเดียวด้วยผู้จัดหาข้อมูล วิธีนี้ช่วยให้วงจรแดง/เขียวรวดเร็ว และทำให้ตารางเจตนาอ่านได้ง่าย
<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\DataProvider;
final class DiscountTableTest extends TestCase
{
#[DataProvider('cases')]
public function test_discounts(float $amount, float $pct, float $expected): void
{
self::assertSame($expected, (new PriceCalculator())->withDiscount($amount, $pct));
}
public static function cases(): array
{
return [
'no discount' => [100.0, 0, 100.0],
'ten percent' => [100.0, 10, 90.0],
'free' => [100.0, 100, 0.0],
];
}
}
รักษาวงจรให้รวดเร็ว
การพัฒนาที่ขับเคลื่อนด้วยการทดสอบจะได้ผลก็ต่อเมื่อวงจรข้อมูลป้อนกลับใช้เวลาเป็นวินาที ไม่ใช่นาที วิธีที่ใช้ได้จริงมีดังนี้:
- เรียกใช้ไฟล์เดียวหรือกรอง:
phpunit --filter test_full_discount_is_free - ใช้
--testdoxเพื่ออ่านการทดสอบให้เป็นข้อกำหนดพฤติกรรม - แยกการทดสอบหน่วยให้ปลอดจากอินพุต/เอาต์พุต — อย่าใช้ฐานข้อมูล เครือข่าย หรือระบบไฟล์ในวงจรด้านใน
vendor/bin/phpunit --testdox --filter PriceCalculatorปรับโครงสร้างการทดสอบด้วย
ระยะปรับโครงสร้างครอบคลุมชุดการทดสอบ ไม่ใช่แค่โค้ดระบบจริง ลบโค้ดซ้ำด้วยตัวช่วยและ setUp() ตั้งชื่อการทดสอบตามพฤติกรรม และลบการทดสอบที่ไม่ตรวจสอบสิ่งมีความหมายอีกต่อไป การทดสอบคือโค้ดที่คุณต้องดูแลไปตลอด — จึงควรใส่ใจในระดับเดียวกัน
<?php
use PHPUnit\Framework\TestCase;
final class PriceCalculatorTest extends TestCase
{
private PriceCalculator $calc;
protected function setUp(): void
{
$this->calc = new PriceCalculator(); // shared setup, no duplication
}
public function test_ten_percent(): void
{
self::assertSame(90.0, $this->calc->withDiscount(100.0, 10));
}
}
การพัฒนาที่ขับเคลื่อนด้วยการทดสอบกำหนดรูปแบบสิ่งที่ต้องพึ่งพา
เพราะคุณเขียนการทดสอบก่อน สิ่งที่ต้องพึ่งพาซึ่งออกแบบไม่ดีจึงเห็นได้ทันที หากสร้างอินสแตนซ์ของคลาสได้ยากในการทดสอบ นั่นคือข้อมูลป้อนกลับด้านการออกแบบ: ให้ฉีดวัตถุที่ทำงานร่วมกันแทนการสร้างด้วย new ภายในคลาส เครื่องคำนวณส่วนลดด้านล่างจะทดสอบได้โดยรับนโยบายการปัดเศษเข้ามา แทนการฝังนโยบายไว้ตายตัว — การพัฒนาที่ขับเคลื่อนด้วยการทดสอบผลักให้เกิดจุดเชื่อมต่อนี้
<?php
interface RoundingPolicy { public function round(float $v): float; }
final class PriceCalculator
{
public function __construct(private RoundingPolicy $rounding) {}
public function withDiscount(float $amount, float $percent): float
{
return $this->rounding->round($amount - ($amount * $percent / 100));
}
}
// Tests inject a deterministic RoundingPolicy; no hidden global behavior.
ตรวจสอบอย่างรวดเร็ว
จุดประสงค์ของขั้นแดงคืออะไร
สรุป
คุณได้ฝึกใช้การพัฒนาที่ขับเคลื่อนด้วยการทดสอบเป็นวินัยด้านการออกแบบ:
- แดง-เขียว-ปรับโครงสร้าง: การทดสอบที่ไม่ผ่าน โค้ดขั้นต่ำ แล้วจึงจัดระเบียบขณะยังผ่านอยู่
- การตรวจสอบด้วยหลายกรณีบังคับให้ครอบคลุมทั่วไปขึ้น ข้อกำหนดใหม่จะมาในรูปการทดสอบใหม่ที่ไม่ผ่าน
- ผู้จัดหาข้อมูลช่วยย่อกรณีที่คงที่ ส่วน
--filter/--testdoxช่วยให้วงจรรวดเร็ว - ปรับโครงสร้างการทดสอบด้วย — การทดสอบเหล่านี้เป็นโค้ดที่มีอายุการใช้งานยาวนาน
ถัดไป: วัตถุทดสอบจำลองที่ยืดหยุ่นด้วย Mockery
คำถามที่พบบ่อย
บทเรียน “เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส PHP Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ”
ขับเคลื่อนการออกแบบด้วยวงจรแดง-เขียว-ปรับโครงสร้าง คุณปฏิบัติ PHP Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน PHP Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน PHP Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน PHP Academy นี้ได้ไหม
ได้ บทเรียน PHP Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ
- การจำลองและการแทนที่ด้วย Mockery
- การทดสอบแบบผสานรวมและเชิงฟังก์ชัน
- การทดสอบการกลายพันธุ์ด้วย Infection