Software Automation Testing คือ —
Automation Testing
Software Automation Testing Unit Integration E2E Performance Selenium Playwright Cypress Jest pytest CI/CD Pipeline Regression Smoke TDD BDD
| Test Type | Scope | Speed | Cost | เครื่องมือ |
|---|---|---|---|---|
| Unit Test | Function | เร็วมาก | ต่ำ | Jest pytest JUnit |
| Integration | Module | ปานกลาง | ปานกลาง | pytest Supertest |
| E2E | ทั้งระบบ | ช้า | สูง | Playwright Cypress |
| Performance | Load/Stress | ช้า | สูง | k6 JMeter Locust |
| API | Endpoint | เร็ว | ต่ำ | Postman Newman |
Unit Test และ Integration
=== Unit Test with pytest ===
pip install pytest pytest-cov
test_calculator.py
class Calculator:
def add(self, a, b):
return a + b
def divide(self, a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
import pytest
class TestCalculator:
def setup_method(self):
self.calc = Calculator()
def test_add(self):
assert self.calc.add(2, 3) == 5
assert self.calc.add(-1, 1) == 0
assert self.calc.add(0, 0) == 0
def test_divide(self):
assert self.calc.divide(10, 2) == 5
assert self.calc.divide(7, 2) == 3.5
def test_divide_by_zero(self):
with pytest.raises(ValueError):
self.calc.divide(10, 0)
@pytest.mark.parametrize("a, b, expected", [
(1, 1, 2),
(2, 3, 5),
(-1, -1, -2),
(100, 200, 300),
])
def test_add_parametrize(self, a, b, expected):
assert self.calc.add(a, b) == expected
Run: pytest test_calculator.py -v --cov=. --cov-report=html
Integration Test — API
import httpx
import pytest
BASE_URL = "http://localhost:8000"
@pytest.fixture
async def client():
async with httpx.AsyncClient(base_url=BASE_URL) as client:
yield client
@pytest.mark.asyncio
async def test_create_user(client):
response = await client.post("/users", json={
"name": "Test User",
"email": "test@example.com"
})
assert response.status_code == 201
data = response.json()
assert data["name"] == "Test User"
@pytest.mark.asyncio
async def test_get_users(client):
response = await client.get("/users")
assert response.status_code == 200
assert isinstance(response.json(), list)
from dataclasses import dataclass
@dataclass
class TestSuite:
name: str
type: str
tests: int
passed: int
failed: int
duration_sec: float
coverage_pct: float
suites = [
TestSuite("Unit Tests", "Unit", 250, 248, 2, 12.5, 85.3),
TestSuite("API Integration", "Integration", 80, 79, 1, 45.0, 72.1),
TestSuite("Database Tests", "Integration", 45, 45, 0, 30.2, 68.5),
TestSuite("E2E Web", "E2E", 35, 34, 1, 180.0, 0),
TestSuite("Performance", "Load", 10, 10, 0, 300.0, 0),
]
print("=== Test Results ===")
total_tests = sum(s.tests for s in suites)
total_passed = sum(s.passed for s in suites)
for s in suites:
status = "PASS" if s.failed == 0 else "FAIL"
print(f" [{status}] {s.name} ({s.type})")
print(f" Tests: {s.passed}/{s.tests} | Duration: {s.duration_sec}s | Coverage: {s.coverage_pct}%")
print(f"\n Total: {total_passed}/{total_tests} passed")
E2E Test
=== E2E Test with Playwright ===
pip install playwright
playwright install
test_login.py
import pytest
from playwright.sync_api import Page, expect
def test_login_success(page: Page):
page.goto("https://app.example.com/login")
page.fill("#email", "user@example.com")
page.fill("#password", "password123")
page.click("button[type=submit]")
expect(page).to_have_url("https://app.example.com/dashboard")
expect(page.locator("h1")).to_have_text("Dashboard")
def test_login_invalid(page: Page):
page.goto("https://app.example.com/login")
page.fill("#email", "wrong@example.com")
page.fill("#password", "wrong")
page.click("button[type=submit]")
expect(page.locator(".error")).to_be_visible()
expect(page.locator(".error")).to_have_text("Invalid credentials")
def test_shopping_cart(page: Page):
page.goto("https://app.example.com/products")
page.click(".product-card:first-child .add-to-cart")
page.click("a[href='/cart']")
expect(page.locator(".cart-items")).to_have_count(1)
page.click("#checkout")
expect(page).to_have_url("/checkout")
Run: pytest test_login.py --headed # visible browser
Run: pytest test_login.py # headless (CI)
Playwright Config
pytest.ini:
[pytest]
addopts = --browser chromium --browser firefox
markers =
smoke: Smoke tests
regression: Full regression
@dataclass
class E2ETest:
name: str
browser: str
steps: int
duration_sec: float
screenshot: bool
status: str
e2e_tests = [
E2ETest("Login Flow", "Chromium", 5, 3.2, True, "Pass"),
E2ETest("Registration", "Chromium", 8, 5.1, True, "Pass"),
E2ETest("Product Search", "Chromium", 4, 2.8, True, "Pass"),
E2ETest("Add to Cart", "Firefox", 6, 4.5, True, "Pass"),
E2ETest("Checkout", "Chromium", 10, 8.3, True, "Pass"),
E2ETest("Payment", "Chromium", 7, 6.1, True, "Fail"),
E2ETest("Admin Dashboard", "Chromium", 5, 3.5, True, "Pass"),
]
print("\n=== E2E Test Results ===")
for t in e2e_tests:
print(f" [{t.status}] {t.name} ({t.browser})")
print(f" Steps: {t.steps} | Duration: {t.duration_sec}s | Screenshot: {t.screenshot}")
CI/CD Pipeline
=== GitHub Actions CI/CD ===
.github/workflows/test.yml
name: Test Pipeline
on: [push, pull_request]
jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: '3.12' }
- run: pip install -r requirements-test.txt
- run: pytest tests/unit -v --cov --cov-report=xml
- uses: codecov/codecov-action@v4
integration-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env: { POSTGRES_PASSWORD: test }
ports: ['5432:5432']
steps:
- uses: actions/checkout@v4
- run: pytest tests/integration -v
e2e-test:
runs-on: ubuntu-latest
needs: [unit-test, integration-test]
steps:
- uses: actions/checkout@v4
- run: pip install playwright pytest-playwright
- run: playwright install --with-deps
- run: pytest tests/e2e --headed=false
Test Strategy
test_strategy = {
"Unit Tests": "ทุก Push, ทุก PR — ต้อง Pass 100%",
"Integration Tests": "ทุก Push, ทุก PR — ต้อง Pass 100%",
"E2E Tests": "ทุก PR ไป main — ต้อง Pass 100%",
"Performance Tests": "Weekly Schedule — Alert ถ้า Regression",
"Security Tests": "Weekly + ก่อน Release",
"Visual Tests": "ทุก PR — Compare Screenshot",
"Code Coverage": "Target 80% — Block PR ถ้าต่ำกว่า",
}
print("Test Strategy:")
for test, strategy in test_strategy.items():
print(f" [{test}]: {strategy}")
Metrics
ci_metrics = {
"Avg Pipeline Duration": "8 minutes",
"Test Pass Rate (30d)": "98.5%",
"Code Coverage": "83%",
"Flaky Test Rate": "1.2%",
"PRs Blocked by Tests": "15/month",
"Bugs Caught by Tests": "28/month",
"Test Count Total": "420",
}
print(f"\n\nCI/CD Metrics:")
for k, v in ci_metrics.items():
print(f" {k}: {v}")
เคล็ดลับ
- Pyramid: Unit มากสุด Integration กลาง E2E น้อยสุด
- CI: รัน Test ทุก Push อัตโนมัติ ไม่ข้าม
- Coverage: ตั้งเป้า 80% Block PR ถ้าต่ำกว่า
- Playwright: ใช้ Playwright สำหรับ E2E ดีกว่า Selenium
- Flaky: แก้ Flaky Test ทันที ไม่ปล่อยผ่าน
Automation Testing คืออะไร
ทดสอบซอฟต์แวร์อัตโนมัติ ลดเวลา ลดข้อผิดพลาด ทำซ้ำ Regression Smoke Selenium Playwright Cypress Jest pytest CI/CD Pipeline