📌

Giới Thiệu Unit Testing

Unit Testing là gì?

Unit Testing là kiểm thử từng đơn vị nhỏ nhất của code (function, method) một cách độc lập. Mục tiêu là đảm bảo mỗi unit hoạt động đúng.

💡 Đặc điểm Unit Test tốt:
Fast: Chạy nhanh (milliseconds)
Isolated: Không phụ thuộc external resources
Repeatable: Chạy bao nhiêu lần cũng như nhau
Self-checking: Tự xác định pass/fail
🐹

Unit Testing với Go

Go Testing Package

Go có testing package built-in, không cần cài thêm.

// calculator.go
package calculator

func Add(a, b int) int {
    return a + b
}

func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("cannot divide by zero")
    }
    return a / b, nil
}
// calculator_test.go
package calculator

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5
    
    if result != expected {
        t.Errorf("Add(2, 3) = %d; want %d", result, expected)
    }
}

// Table-driven tests
func TestDivide(t *testing.T) {
    tests := []struct {
        name    string
        a, b    int
        want    int
        wantErr bool
    }{
        {"positive", 10, 2, 5, false},
        {"zero divisor", 10, 0, 0, true},
        {"negative", -10, 2, -5, false},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := Divide(tt.a, tt.b)
            if (err != nil) != tt.wantErr {
                t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if got != tt.want {
                t.Errorf("Divide() = %v, want %v", got, tt.want)
            }
        })
    }
}
# Chạy tests
go test ./...

# Với coverage
go test -cover ./...

# Verbose output
go test -v ./...
🐍

Unit Testing với Python

pytest

pytest là framework phổ biến nhất cho Python testing.

# calculator.py
def add(a: int, b: int) -> int:
    return a + b

def divide(a: int, b: int) -> float:
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b
# test_calculator.py
import pytest
from calculator import add, divide

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

def test_divide():
    assert divide(10, 2) == 5.0
    assert divide(-10, 2) == -5.0

def test_divide_by_zero():
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        divide(10, 0)

# Parametrized tests
@pytest.mark.parametrize("a,b,expected", [
    (2, 3, 5),
    (-1, 1, 0),
    (100, 200, 300),
])
def test_add_parametrized(a, b, expected):
    assert add(a, b) == expected
# Cài pytest
pip install pytest pytest-cov

# Chạy tests
pytest

# Với coverage
pytest --cov=. --cov-report=html

Unit Testing với JavaScript

Jest

Jest là testing framework phổ biến nhất cho JavaScript/TypeScript.

// calculator.js
export function add(a, b) {
    return a + b;
}

export function divide(a, b) {
    if (b === 0) {
        throw new Error("Cannot divide by zero");
    }
    return a / b;
}
// calculator.test.js
import { add, divide } from './calculator';

describe('Calculator', () => {
    describe('add', () => {
        test('adds two positive numbers', () => {
            expect(add(2, 3)).toBe(5);
        });

        test('adds negative numbers', () => {
            expect(add(-1, 1)).toBe(0);
        });
    });

    describe('divide', () => {
        test('divides two numbers', () => {
            expect(divide(10, 2)).toBe(5);
        });

        test('throws error when dividing by zero', () => {
            expect(() => divide(10, 0)).toThrow('Cannot divide by zero');
        });
    });

    // Parameterized tests
    test.each([
        [2, 3, 5],
        [-1, 1, 0],
        [100, 200, 300],
    ])('add(%i, %i) returns %i', (a, b, expected) => {
        expect(add(a, b)).toBe(expected);
    });
});
# Cài Jest
npm install --save-dev jest

# Chạy tests
npm test

# Với coverage
npm test -- --coverage

Best Practices

AAA Pattern

Mỗi test nên theo pattern Arrange-Act-Assert:

func TestUserCreation(t *testing.T) {
    // Arrange - Setup test data
    name := "John Doe"
    email := "[email protected]"
    
    // Act - Execute the code under test
    user, err := CreateUser(name, email)
    
    // Assert - Verify the results
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
    if user.Name != name {
        t.Errorf("got name %q, want %q", user.Name, name)
    }
}

Test Naming

💡 Đặt tên test rõ ràng:
TestAdd_PositiveNumbers_ReturnsSum
test_divide_by_zero_raises_error
should return error when input is invalid

Coverage Goals

⚠️ Lưu ý về Coverage:
• 80% coverage là mục tiêu tốt cho hầu hết projects
• 100% coverage không có nghĩa là code không có bug
• Focus vào test quality, không chỉ quantity