#1. ทำไมต้องทำ Unit Test

ประโยชน์หลักของ Unit Test ได้แก่

  • ตรวจสอบ logic ของโปรแกรมแบบอัตโนมัติ
  • ลด regression bug เมื่อมีการแก้ไขโค้ด
  • ช่วยให้ refactor ได้มั่นใจมากขึ้น
  • ใช้เป็น executable specification ของระบบ
  • ทำงานร่วมกับ CI/CD ได้ดี
  • ช่วยออกแบบโค้ดให้มี coupling ต่ำและ testability สูงขึ้น

โครงสร้างพื้นฐานของ Unit Test มักเขียนในรูปแบบ Arrange – Act – Assert (AAA)

Arrange -> เตรียมข้อมูลและ dependency
Act     -> เรียกฟังก์ชันหรือเมธอดที่ต้องการทดสอบ
Assert  -> ตรวจสอบผลลัพธ์

ตัวอย่างแนวคิด

Input:  add(2, 3)
Expected: 5

#2. Framework แยกตามภาษา

#JavaScript / TypeScript

#Vitest

Vitest เป็น testing framework ที่ทำงานร่วมกับ ecosystem ของ Vite ได้โดยตรง เหมาะกับโครงการสมัยใหม่ที่ใช้ JavaScript หรือ TypeScript โดยเฉพาะ React, Vue และ frontend stack ที่ใช้ Vite

จุดเด่น

  • syntax คล้าย Jest
  • ทำงานเร็วและ integrate กับ Vite ได้ดี
  • รองรับ TypeScript
  • mocking
  • snapshot testing
  • code coverage
  • watch mode

ติดตั้ง

npm install -D vitest

ตัวอย่าง

import { describe, it, expect } from 'vitest'

function add(a: number, b: number) {
  return a + b
}

describe('add()', () => {
  it('should return 5', () => {
    expect(add(2, 3)).toBe(5)
  })
})

รัน

npx vitest

เหมาะกับ

  • React + Vite
  • Vue + Vite
  • Node.js
  • TypeScript

#Jest

Jest เป็น testing framework ที่มี ecosystem ใหญ่ และใช้แพร่หลายใน JavaScript/TypeScript

จุดเด่น

  • assertions ในตัว
  • mocking
  • snapshot testing
  • test isolation
  • parallel execution
  • ecosystem ใหญ่

ตัวอย่าง

function multiply(a, b) {
  return a * b
}

test('2 * 3 = 6', () => {
  expect(multiply(2, 3)).toBe(6)
})

เหมาะกับ

  • Node.js
  • React
  • JavaScript/TypeScript project เดิมที่ใช้ Jest อยู่แล้ว

#React Testing Library

React Testing Library ไม่ใช่ test runner โดยตัวมันเอง แต่เป็น utility สำหรับทดสอบ React Component โดยเน้นพฤติกรรมที่ผู้ใช้มองเห็น

มักใช้งานร่วมกับ

Vitest + React Testing Library

หรือ

Jest + React Testing Library

ตัวอย่าง

render(<LoginButton />)

expect(
  screen.getByRole('button', { name: /login/i })
).toBeInTheDocument()

#Python

#pytest

pytest เป็น framework ยอดนิยมของ Python เพราะ syntax สั้น อ่านง่าย และมี plugin ecosystem ขนาดใหญ่

จุดเด่น

  • fixtures
  • parametrization
  • mocking ผ่าน plugin หรือ unittest.mock
  • plugin ecosystem ใหญ่
  • integration กับ coverage และ CI ง่าย

ติดตั้ง

pip install pytest

ตัวอย่าง

def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5

รัน

pytest

ตัวอย่าง Parameterized Test

import pytest

@pytest.mark.parametrize(
    "a,b,expected",
    [
        (1, 2, 3),
        (2, 3, 5),
        (10, 20, 30),
    ],
)
def test_add(a, b, expected):
    assert a + b == expected

เหมาะกับ

  • Python application
  • FastAPI
  • Flask
  • Django
  • Data Science / ML utility code

#unittest

unittest เป็น testing framework ที่มากับ Python Standard Library และได้รับแนวคิดจาก xUnit/JUnit style

ตัวอย่าง

import unittest

class CalculatorTest(unittest.TestCase):

    def test_add(self):
        self.assertEqual(2 + 3, 5)

if __name__ == '__main__':
    unittest.main()

ข้อดีคือไม่ต้องติดตั้ง dependency เพิ่ม


#Java

#JUnit

JUnit เป็นมาตรฐานหลักของ Java ecosystem สำหรับการทำ Unit Test และทำงานร่วมกับ Maven, Gradle และ IDE ได้ดี

ปัจจุบัน JUnit รุ่นใหม่ประกอบด้วยแนวคิดหลัก เช่น JUnit Platform และ JUnit Jupiter

ตัวอย่าง

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

class CalculatorTest {

    @Test
    void addShouldReturnFive() {
        assertEquals(5, 2 + 3);
    }
}

Maven

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <scope>test</scope>
</dependency>

รัน

mvn test

เหมาะกับ

  • Java
  • Spring Boot
  • Quarkus
  • Jakarta EE

#Mockito

Mockito ไม่ใช่ Unit Test Framework หลัก แต่เป็น mocking framework ที่นิยมใช้คู่กับ JUnit

ตัวอย่าง

UserRepository repository = mock(UserRepository.class);

when(repository.findById(1L))
    .thenReturn(Optional.of(user));

รูปแบบที่พบบ่อย

JUnit + Mockito

#Kotlin

#Kotest

Kotest เป็น testing framework สำหรับ Kotlin ที่รองรับหลาย test style รวมถึง property-based testing

ตัวอย่างแนวคิด

class CalculatorTest : StringSpec({

    "2 + 3 should be 5" {
        2 + 3 shouldBe 5
    }

})

เหมาะกับ

  • Kotlin
  • Kotlin Multiplatform
  • Backend Kotlin

บน JVM สามารถทำงานบน JUnit Platform ได้


#PHP

#PHPUnit

PHPUnit เป็น testing framework หลักของ PHP ecosystem

ตัวอย่าง

use PHPUnit\Framework\TestCase;

class CalculatorTest extends TestCase
{
    public function testAdd(): void
    {
        $this->assertEquals(5, 2 + 3);
    }
}

รัน

./vendor/bin/phpunit

เหมาะกับ

  • PHP
  • Laravel
  • Symfony
  • CodeIgniter

#Pest

Pest เป็น testing framework สำหรับ PHP ที่ให้ syntax กระชับและอ่านง่าย และทำงานบน PHPUnit

ตัวอย่าง

test('2 + 3 = 5', function () {
    expect(2 + 3)->toBe(5);
});

รัน

./vendor/bin/pest

สำหรับ Laravel รูปแบบที่นิยมคือ

Laravel + Pest

หรือ

Laravel + PHPUnit

#C# / .NET

#xUnit.net

xUnit.net เป็น testing framework ที่นิยมมากใน .NET ecosystem

ตัวอย่าง

public class CalculatorTests
{
    [Fact]
    public void Add_ReturnsFive()
    {
        Assert.Equal(5, 2 + 3);
    }
}

รัน

dotnet test

#NUnit

NUnit เป็น framework ที่ได้รับแนวคิดจาก xUnit family และรองรับ .NET อย่างกว้างขวาง

ตัวอย่าง

[TestFixture]
public class CalculatorTests
{
    [Test]
    public void Add_ReturnsFive()
    {
        Assert.That(2 + 3, Is.EqualTo(5));
    }
}

#MSTest

MSTest เป็น testing framework ของ Microsoft สำหรับ .NET

ตัวอย่าง

[TestClass]
public class CalculatorTests
{
    [TestMethod]
    public void Add_ReturnsFive()
    {
        Assert.AreEqual(5, 2 + 3);
    }
}

เหมาะกับองค์กรที่ใช้ Microsoft ecosystem และ Visual Studio


#Go

#testing package

Go มี testing framework อยู่ใน Standard Library โดยตรง จึงไม่จำเป็นต้องติดตั้ง framework เพิ่มสำหรับ Unit Test พื้นฐาน

ตัวอย่าง

package calculator

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)

    if result != 5 {
        t.Errorf("expected 5, got %d", result)
    }
}

รัน

go test ./...

แนวทางที่นิยมมากใน Go คือ Table-Driven Test

func TestAdd(t *testing.T) {
    tests := []struct {
        a        int
        b        int
        expected int
    }{
        {1, 2, 3},
        {2, 3, 5},
        {10, 20, 30},
    }

    for _, tt := range tests {
        result := Add(tt.a, tt.b)

        if result != tt.expected {
            t.Errorf("expected %d, got %d", tt.expected, result)
        }
    }
}

#Rust

#cargo test + libtest

Rust มี test support มากับ toolchain โดยใช้ attribute #[test]

ตัวอย่าง

fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 3), 5);
    }
}

รัน

cargo test

Rust แยก unit test และ integration test ได้ชัดเจน โดย integration test มักอยู่ในโฟลเดอร์ tests/


#3. ตารางเปรียบเทียบ Framework

ภาษา / Platform Framework เหมาะกับ จุดเด่น
JavaScript / TypeScript Vitest Vite, React, Vue เร็ว, Vite-native
JavaScript / TypeScript Jest Node, React ecosystem ใหญ่
React React Testing Library Component Test ทดสอบผ่านพฤติกรรมผู้ใช้
Python pytest Python app, FastAPI, ML fixture, plugin, parametrize
Python unittest Python Standard Library ไม่ต้องติดตั้งเพิ่ม
Java JUnit Java, Spring Boot มาตรฐาน Java
Java Mockito Mock dependency mocking ยอดนิยม
Kotlin Kotest Kotlin, KMP DSL, property testing
PHP PHPUnit PHP มาตรฐาน PHP
PHP Pest Laravel / PHP syntax กระชับ
.NET xUnit.net ASP.NET Core, .NET modern .NET ecosystem
.NET NUnit .NET mature ecosystem
.NET MSTest Microsoft stack integration กับ Microsoft tools
Go testing Go built-in
Rust cargo test / libtest Rust built-in

#4. เลือก Framework อย่างไร

หลักการเลือกควรเริ่มจาก ecosystem ของภาษาและ framework ที่ใช้จริง

React + Vite
     |
     v
Vitest + React Testing Library
Node.js / TypeScript
     |
     +--> Vitest
     |
     +--> Jest
Python
     |
     +--> pytest        <- แนะนำสำหรับโครงการใหม่ทั่วไป
     |
     +--> unittest      <- built-in
Java / Spring Boot
     |
     v
JUnit + Mockito
Laravel
     |
     +--> Pest
     |
     +--> PHPUnit
ASP.NET Core
     |
     +--> xUnit.net
     +--> NUnit
     +--> MSTest
Go
 |
 v
testing package
Rust
 |
 v
cargo test

#5. Unit Test Framework กับ Mocking Framework ต่างกันอย่างไร

สองอย่างนี้ไม่เหมือนกัน

#Unit Test Framework

มีหน้าที่

  • ค้นหา test
  • รัน test
  • assertions
  • lifecycle
  • report result

ตัวอย่าง

JUnit
pytest
Vitest
Jest
PHPUnit
xUnit.net
NUnit
MSTest

#Mocking Framework

ใช้จำลอง dependency

ตัวอย่าง

Mockito
Moq
unittest.mock
vi.mock()
jest.mock()

ตัวอย่าง architecture

                Unit Test

                  |
                  v

             UserService
             /         \
            /           \
     UserRepository    EmailService
          |                 |
          v                 v
        Mock              Mock

หลักสำคัญคือ Unit Test ควรพยายามไม่พึ่งพา

Database
External API
File System
Network
Message Queue

#6. ตัวอย่าง Test Pyramid

                 /\
                /  \
               / E2E\
              /------\
             /Integration\
            /------------\
           /  Unit Tests  \
          /________________\

โดยทั่วไปควรมี Unit Test จำนวนมากกว่า Integration Test และ E2E Test เพราะ Unit Test เร็วและให้ feedback ได้เร็วกว่า


#7. แนวทางที่แนะนำในปี 2026

สำหรับโครงการใหม่ สามารถใช้ baseline ดังนี้

Technology Suggested Stack
React + Vite Vitest + React Testing Library
Node.js Vitest หรือ Jest
Python pytest
Java / Spring Boot JUnit + Mockito
Kotlin Kotest หรือ JUnit
Laravel Pest หรือ PHPUnit
ASP.NET Core xUnit.net / NUnit / MSTest
Go testing package
Rust cargo test

#8. การนำ Unit Test เข้า CI/CD

ตัวอย่าง pipeline

Developer
   |
   v
git push
   |
   v
CI Pipeline
   |
   +--> Install Dependencies
   |
   +--> Lint
   |
   +--> Unit Test
   |
   +--> Code Coverage
   |
   +--> Build
   |
   v
Deploy

ตัวอย่าง GitHub Actions

name: Unit Test

on:
  push:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - run: npm ci

      - run: npm test

#9. Code Coverage

Unit Test มักใช้ร่วมกับ Code Coverage

ตัวอย่างเครื่องมือ

ภาษา Coverage Tool
JavaScript / TypeScript Vitest Coverage, Jest Coverage, Istanbul
Python coverage.py, pytest-cov
Java JaCoCo
PHP PHPUnit Coverage + Xdebug/PCOV
.NET Coverlet
Go go test -cover
Rust cargo-llvm-cov

ไม่ควรตีความว่า Coverage สูง = Software Quality สูงเสมอไป

สิ่งสำคัญคือ test ต้องตรวจสอบ behavior และ business logic ที่สำคัญอย่างมีความหมาย


#10. สรุป

ไม่มี Unit Test Framework ตัวเดียวที่ดีที่สุดสำหรับทุกภาษา ควรเลือกให้ตรงกับ ecosystem ของโครงการ

แนวทางที่ใช้ได้ดีคือ

JavaScript/TypeScript -> Vitest / Jest
Python                -> pytest
Java                  -> JUnit + Mockito
Kotlin                -> Kotest / JUnit
PHP/Laravel            -> Pest / PHPUnit
.NET                   -> xUnit.net / NUnit / MSTest
Go                     -> testing
Rust                   -> cargo test

หากต้องการเริ่มต้นให้ได้ผลเร็ว ควรเริ่มจาก

  1. ทดสอบ business logic ก่อน
  2. เขียน test ให้เป็นอิสระต่อกัน
  3. Mock เฉพาะ dependency ภายนอก
  4. ใช้ AAA หรือ Given–When–Then
  5. รัน test ทุกครั้งใน CI
  6. เก็บ coverage เป็น metric ประกอบ ไม่ใช่เป้าหมายเดียว

#References