PlusMagi's Blog By Pitt Phunsanit Backend,Cloud Computing Go Testing, Benchmarking & Tooling (การเขียน Unit Test, Table-Driven Tests, Benchmark และเครื่องมือ Profiling เช่น pprof ใน Go)

Go Testing, Benchmarking & Tooling (การเขียน Unit Test, Table-Driven Tests, Benchmark และเครื่องมือ Profiling เช่น pprof ใน Go)

ในฐานะ Senior Architect การพัฒนาซอฟต์แวร์ที่ยอดเยี่ยมไม่ได้วัดกันแค่ที่ฟังก์ชันที่ทำงานได้ แต่รวมถึงความมั่นคง (Robustness) และประสิทธิภาพ (Performance) ด้วย บทความนี้คือคู่มือเชิงลึกสำหรับนักพัฒนาระดับสูงที่ต้องการยกระดับการทดสอบระบบ Go (Golang) ให้เหนือกว่าการเขียน Unit Test พื้นฐาน เราจะเจาะลึกตั้งแต่เทคนิค Table-Driven Tests ไปจนถึงการวิเคราะห์โปรไฟล์ด้วย pprof เพื่อให้มั่นใจว่าโค้ดของคุณพร้อมสำหรับการใช้งานจริงในระดับ Production

🧪 Unit Testing: การตรวจสอบความถูกต้องของหน่วยย่อย

Unit Test คือการทดสอบส่วนที่เล็กที่สุดของระบบ (เช่น ฟังก์ชันเดียว) โดยแยกมันออกจากส่วนอื่น ๆ เพื่อยืนยันว่าส่วนนั้นทำงานถูกต้องตามข้อกำหนด (Specification) การใช้แพ็คเกจมาตรฐาน testing ของ Go เป็นพื้นฐานที่สำคัญที่สุด

💡 Best Practice: Table-Driven Tests

แทนที่จะเขียน Test Case แยกกันหลายฟังก์ชัน เราควรใช้รูปแบบ Table-Driven Tests ซึ่งเป็นการรวมชุดข้อมูล Input และ Expected Output ไว้ในโครงสร้างข้อมูล (struct) เดียว ทำให้โค้ดทดสอบมีความกระชับ อ่านง่าย และสามารถขยาย Test Case ได้อย่างรวดเร็วเมื่อระบบมีการเปลี่ยนแปลง

Go

package main

import "testing"

// ฟังก์ชันที่ต้องการทดสอบ
func Add(a, b int) int {
    return a + b
}

func TestAdd(t *testing.T) {
    // กำหนดชุดข้อมูลทดสอบ
    tests := []struct {
        name string
        a    int
        b    int
        want int
    }{
        {"Positive Numbers", 1, 2, 3},
        {"Negative Numbers", -1, -2, -3},
        {"Zero Case", 0, 5, 5},
    }

    // วนลูปเพื่อรัน Test Case ทั้งหมด
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := Add(tt.a, tt.b)
            if got != tt.want {
                t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, got, tt.want)
            }
        })
    }
}

🚀 Benchmarking: การวัดประสิทธิภาพโค้ด

เมื่อโค้ดทำงานได้ถูกต้องแล้ว ขั้นต่อไปคือการวัดว่ามันทำงานได้เร็วแค่ไหน เราใช้ Benchmarking ซึ่งอาศัยแพ็คเกจ testing.B เพื่อวัดเวลาที่ใช้ในการทำงานซ้ำ ๆ (Iteration) ของโค้ดส่วนใดส่วนหนึ่ง

Go

func BenchmarkAdd(b *testing.B) {
    // b.N คือจำนวนรอบที่ Go Test Runner กำหนดให้รัน
    for i := 0; i < b.N; i++ {
        Add(10, 20)
    }
}

🔬 Profiling ด้วย pprof: เจาะลึกคอขวดของระบบ

สำหรับระบบขนาดใหญ่ การวัดเวลาอย่างเดียวไม่พอ เราต้องรู้ว่าทรัพยากร (CPU, Memory) ถูกใช้ไปที่ส่วนไหนบ้าง pprof คือเครื่องมือ Profiling ที่ทรงพลังที่สุดของ Go มันช่วยให้เราสร้างโปรไฟล์ที่ระบุได้อย่างแม่นยำว่าฟังก์ชันใดใช้ CPU มากเกินไป หรือส่วนใดมีการรั่วไหลของหน่วยความจำ (Memory Leak) ทำให้เราสามารถแก้ไขปัญหาคอขวด (Bottleneck) ได้อย่างตรงจุด

✅ บทสรุป: สร้างระบบที่แข็งแกร่งด้วยวงจรการทดสอบครบวงจร

การรวมกันของ Unit Testing (ความถูกต้อง), Table-Driven Tests (ความยืดหยุ่น), Benchmarking (ประสิทธิภาพ) และ pprof (การวิเคราะห์เชิงลึก) ทำให้เราสามารถสร้างระบบ Go ที่ไม่เพียงแต่ใช้งานได้ แต่ยังทำงานได้อย่างรวดเร็วและเสถียรในทุกสภาวะการใช้งาน การยกระดับกระบวนการทดสอบเหล่านี้คือสิ่งที่แยกโค้ดระดับมือสมัครเล่นออกจากโค้ดระดับ Senior Architect อย่างแท้จริง