SiamCafe · Blog
Software Automation Testing คือ —
บทความ

Software Automation Testing คือ —

เผยแพร่ 28 พฤษภาคม 2569

Automation Testing

Software Automation Testing Unit Integration E2E Performance Selenium Playwright Cypress Jest pytest CI/CD Pipeline Regression Smoke TDD BDD

Test TypeScopeSpeedCostเครื่องมือ
Unit TestFunctionเร็วมากต่ำJest pytest JUnit
IntegrationModuleปานกลางปานกลางpytest Supertest
E2Eทั้งระบบช้าสูงPlaywright Cypress
PerformanceLoad/Stressช้าสูงk6 JMeter Locust
APIEndpointเร็วต่ำ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