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
มี Test ประเภทอะไรบ้าง
Unit Function เร็ว Integration Module Database API E2E ทั้งระบบ Performance Load Security Visual Test Pyramid Unit มาก E2E น้อย
เครื่องมือ Automation Testing มีอะไรบ้าง
Selenium ทุก Browser Playwright เร็ว Chrome Firefox Safari Cypress E2E Web Jest JavaScript pytest Python JUnit Java k6 Performance Postman API Appium Mobile
เริ่มทำ Automation Testing อย่างไร
Unit Test ก่อน pytest Jest Integration API Database Playwright E2E CI/CD GitHub Actions Coverage 80% TDD เขียน Test ก่อน Code
สรุป
Software Automation Testing Unit Integration E2E Selenium Playwright Cypress pytest Jest CI/CD GitHub Actions Coverage TDD Test Pyramid Performance Regression
อ่านเพิ่มเติม: สอนเทรด Forex | XM Signal | IT Hardware | อาชีพ IT
