본문으로 건너뛰기

เนื้อหาภาษาไทย / 태국어 콘텐츠

시스템 가이드 검수 완료된 콘텐츠

Test Driven Development (TDD) เบื้องต้น

บทความนี้แนะนำ Test Driven Development (TDD) ซึ่งเป็นกระบวนการพัฒนาซอฟต์แวร์ที่เน้นการเขียน Test ก่อนเขียน Code จริงตามวงจร Red-Green-Refactor โดย TDD มีประโยชน...

5. Test Driven Development (TDD) เบื้องต้น

ในบทที่แล้ว เราได้เรียนรู้วิธีการรัน Test Case ที่เราสร้างขึ้นด้วย PHPUnit คราวนี้ เราจะมาทำความรู้จักกับแนวคิดที่น่าสนใจและมีประโยชน์อย่างมากในการพัฒนาซอฟต์แวร์ นั่นคือ Test Driven Development หรือ TDD

TDD คืออะไร?

Test Driven Development (TDD) คือกระบวนการพัฒนาซอฟต์แวร์ที่เน้นการเขียน Test ก่อน ที่จะเขียน Code จริง ที่จะทำให้ Test นั้นผ่าน (Pass) ฟังดูแปลกใช่ไหมครับ? ปกติเราจะเขียน Code ก่อน แล้วค่อยเขียน Test เพื่อตรวจสอบว่า Code ของเราทำงานถูกต้อง แต่ TDD ทำตรงกันข้าม

TDD ประกอบด้วยวงจรที่เรียกว่า Red-Green-Refactor ซึ่งเป็นหัวใจสำคัญของกระบวนการนี้

  • Red: เขียน Test ที่ล้มเหลว (Fail) ก่อน เพราะเรายังไม่มี Code ที่จะทำให้ Test ผ่าน
  • Green: เขียน Code ที่น้อยที่สุด (Simplest Code) ที่จะทำให้ Test ที่ล้มเหลวในขั้นตอน Red ผ่าน
  • Refactor: ปรับปรุง Code ที่เราเขียนในขั้นตอน Green ให้ดีขึ้น (Clean Code) โดยยังคงให้ Test ทั้งหมดผ่าน

เราจะวนซ้ำในวงจร Red-Green-Refactor นี้ไปเรื่อยๆ จนกว่าเราจะได้ Code ที่ทำงานได้ตามความต้องการและมีคุณภาพดี

Red-Green-Refactor: ลงรายละเอียด

เพื่อให้เข้าใจ Red-Green-Refactor มากขึ้น เราจะอธิบายแต่ละขั้นตอนอย่างละเอียดพร้อมตัวอย่าง

Red: เขียน Test ที่ล้มเหลว

ในขั้นตอนนี้ เราจะเขียน Test Case ที่เราคาดหวังว่า Code ที่เราจะเขียนในอนาคตควรจะทำงานอย่างไร Test นี้จะล้มเหลว (Red) เพราะเรายังไม่มี Code ที่จะทำให้มันผ่าน

ตัวอย่าง: สมมติว่าเราต้องการสร้างฟังก์ชันที่คำนวณพื้นที่สี่เหลี่ยมผืนผ้า เราจะเขียน Test Case ก่อนดังนี้

use PHPUnit\Framework\TestCase;

class RectangleAreaCalculatorTest extends TestCase
{
    public function testCalculateArea()
    {
        $width = 5;
        $height = 10;
        $expectedArea = 50;

        $calculator = new RectangleAreaCalculator();
        $actualArea = $calculator->calculateArea($width, $height);

        $this->assertEquals($expectedArea, $actualArea);
    }
}

ใน Test Case นี้ เราคาดหวังว่าเมื่อเราเรียกฟังก์ชัน `calculateArea()` ด้วยความกว้าง 5 และความสูง 10 ฟังก์ชันจะคืนค่า 50 (พื้นที่สี่เหลี่ยมผืนผ้า) แน่นอนว่า Test นี้จะล้มเหลวเพราะเรายังไม่มี Class `RectangleAreaCalculator` และฟังก์ชัน `calculateArea()`

เมื่อรัน Test นี้ด้วย `phpunit`, PHPUnit จะแจ้งว่า Class `RectangleAreaCalculator` ไม่พบ

Green: เขียน Code ที่ทำให้ Test ผ่าน

ในขั้นตอนนี้ เราจะเขียน Code ที่น้อยที่สุด (Simplest Code) ที่จะทำให้ Test ที่ล้มเหลวในขั้นตอน Red ผ่าน ไม่ต้องกังวลเรื่องความสวยงามหรือประสิทธิภาพในตอนนี้ เป้าหมายคือทำให้ Test ผ่านเท่านั้น

ตัวอย่าง: เพื่อให้ Test Case ของเราผ่าน เราจะสร้าง Class `RectangleAreaCalculator` และฟังก์ชัน `calculateArea()` ดังนี้

class RectangleAreaCalculator
{
    public function calculateArea(int $width, int $height): int
    {
        return $width * $height;
    }
}

ตอนนี้เมื่อเรารัน Test อีกครั้งด้วย `phpunit` Test Case ของเราจะผ่าน (Green)

Refactor: ปรับปรุง Code

ในขั้นตอนนี้ เราจะปรับปรุง Code ที่เราเขียนในขั้นตอน Green ให้ดีขึ้น (Clean Code) โดยยังคงให้ Test ทั้งหมดผ่าน เราอาจจะปรับปรุงเรื่องความสวยงาม ประสิทธิภาพ หรือความสามารถในการอ่าน

ตัวอย่าง: ในตัวอย่างของเรา Code ที่เราเขียนในขั้นตอน Green ค่อนข้างเรียบง่ายและอ่านง่ายอยู่แล้ว แต่ถ้าสมมติว่าเราต้องการเพิ่มการตรวจสอบว่าความกว้างและความสูงต้องเป็นค่าบวก เราจะทำการ Refactor ดังนี้

class RectangleAreaCalculator
{
    public function calculateArea(int $width, int $height): int
    {
        if ($width <= 0 || $height <= 0) {
            throw new InvalidArgumentException("Width and height must be positive.");
        }
        return $width * $height;
    }
}

และเราต้องเพิ่ม Test Case เพื่อตรวจสอบ Exception ที่เราเพิ่มเข้ามา

use PHPUnit\Framework\TestCase;
use InvalidArgumentException;

class RectangleAreaCalculatorTest extends TestCase
{
    public function testCalculateArea()
    {
        $width = 5;
        $height = 10;
        $expectedArea = 50;

        $calculator = new RectangleAreaCalculator();
        $actualArea = $calculator->calculateArea($width, $height);

        $this->assertEquals($expectedArea, $actualArea);
    }

    public function testCalculateAreaThrowsExceptionForInvalidDimensions()
    {
        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage("Width and height must be positive.");

        $calculator = new RectangleAreaCalculator();
        $calculator->calculateArea(-5, 10);
    }
}

หลังจาก Refactor และเพิ่ม Test Case แล้ว เราต้องรัน Test ทั้งหมดอีกครั้งเพื่อให้แน่ใจว่า Code ของเรายังคงทำงานได้อย่างถูกต้อง

การเขียน Test ก่อน Code

หัวใจสำคัญของ TDD คือการเขียน Test ก่อนที่จะเขียน Code จริง การทำเช่นนี้มีประโยชน์หลายประการ:

  • ช่วยให้เราเข้าใจความต้องการได้ชัดเจน: การเขียน Test ก่อนทำให้เราต้องคิดอย่างละเอียดเกี่ยวกับสิ่งที่ Code ของเราควรจะทำ ทำให้เราเข้าใจความต้องการได้ชัดเจนมากขึ้น
  • ช่วยให้เราออกแบบ API ที่ดี: การเขียน Test ก่อนทำให้เราต้องคิดถึง API ของเราในมุมมองของผู้ใช้งาน ทำให้เราออกแบบ API ที่ใช้งานง่ายและเป็นธรรมชาติ
  • ช่วยลด Bug: การเขียน Test ก่อนทำให้เราสามารถตรวจจับ Bug ได้ตั้งแต่เนิ่นๆ ก่อนที่ Bug เหล่านั้นจะกลายเป็นปัญหาใหญ่ใน Production
  • ช่วยให้เรา Refactor ได้อย่างมั่นใจ: การมี Test ที่ครอบคลุมทำให้เราสามารถ Refactor Code ได้อย่างมั่นใจ เพราะเรารู้ว่าถ้าเราทำอะไรผิดพลาด Test จะแจ้งให้เราทราบ
  • ช่วยให้เราเขียน Code ที่ Testable: การเขียน Test ก่อนทำให้เราเขียน Code ที่ง่ายต่อการ Test เพราะเราต้องคิดถึงวิธีการ Test ตั้งแต่เริ่มต้น

ประโยชน์ของ TDD

นอกเหนือจากที่กล่าวมาแล้ว TDD ยังมีประโยชน์อื่นๆ อีกมากมาย:

  • Code Coverage ที่ดี: TDD มักจะนำไปสู่ Code Coverage ที่สูง เพราะเราเขียน Test สำหรับทุก Functionality
  • Documentation ที่ดี: Test Case สามารถใช้เป็น Documentation ที่แสดงให้เห็นว่า Code ของเราทำงานอย่างไร
  • ความมั่นใจในการ Deploy: การมี Test ที่ครอบคลุมทำให้เรามั่นใจในการ Deploy Code ไปยัง Production
  • ลดค่าใช้จ่ายในการบำรุงรักษา: Code ที่เขียนด้วย TDD มักจะง่ายต่อการบำรุงรักษา เพราะมี Test ที่ช่วยให้เราเข้าใจ Code ได้ง่ายขึ้น

สรุป

Test Driven Development (TDD) เป็นกระบวนการพัฒนาซอฟต์แวร์ที่เน้นการเขียน Test ก่อนที่จะเขียน Code จริง โดยมีวงจร Red-Green-Refactor เป็นหัวใจสำคัญ TDD มีประโยชน์มากมาย เช่น ช่วยให้เราเข้าใจความต้องการได้ชัดเจน ออกแบบ API ที่ดี ลด Bug Refactor ได้อย่างมั่นใจ และอื่นๆ ถึงแม้ว่า TDD อาจจะดูเหมือนเสียเวลาในตอนแรก แต่ในระยะยาวมันจะช่วยประหยัดเวลาและค่าใช้จ่ายได้มาก

ในบทต่อไป เราจะมาเรียนรู้เกี่ยวกับการ Mocking ซึ่งเป็นเทคนิคที่ใช้ในการจำลอง Dependencies ใน Unit Testing

Office.in.th Custom Service

이 구조를 바탕으로 시스템을 개발하거나 맞춤 변경하고 싶으신가요?

Office.in.th는 엔터프라이즈 웹 애플리케이션, API 연동, 공공/민간 업무 관리 시스템을 설계·개발하며 장기 지원을 제공합니다.

소프트웨어 개발 서비스 보기