0Pricing
PHP Academy · บทเรียน

เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ

ขับเคลื่อนการออกแบบด้วยวงจรแดง-เขียว-ปรับโครงสร้าง

เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เวิร์กโฟลว์การพัฒนาโดยขับเคลื่อนด้วยการทดสอบ
  2. การจำลองและการแทนที่ด้วย Mockery
  3. การทดสอบแบบผสานรวมและเชิงฟังก์ชัน
  4. การทดสอบการกลายพันธุ์ด้วย Infection
← กลับไปที่ PHP Academy