# สัปดาห์ที่ 11: Test Automation (การทดสอบแบบอัตโนมัติ)

## 🎯 วัตถุประสงค์การเรียนรู้ (Learning Objectives)

- อธิบายเมื่อไหร่ควร automate tests และเมื่อไหร่ควรทำ manual testing (Explain when to automate)
- เข้าใจ Test Automation Pyramid และ anti-patterns (Understand pyramid)
- คำนวณ ROI ของ test automation (Calculate ROI)
- ออกแบบ test automation framework ด้วย Page Object Model (Design framework)
- เขียน UI automation tests ด้วย Playwright (Write UI tests)
- เขียน API automation tests ด้วย supertest (Write API tests)
- ประยุกต์ใช้ best practices ในการเขียน automated tests (Apply best practices)

---

## ส่วนที่ 1: Test Automation Fundamentals - พื้นฐานของการทดสอบแบบอัตโนมัติ

### 1.1 What is Test Automation? - การทดสอบแบบอัตโนมัติคืออะไร

**Test Automation** คือการใช้ software tools และ scripts ในการทดสอบซอฟต์แวร์แบบอัตโนมัติ แทนการทดสอบด้วยมือ (manual testing)

**ข้อดี (Advantages):**

- **ทำงานเร็วกว่า** (Faster) - รันได้หลายพันครั้งต่อวัน
- **ทำซ้ำได้** (Repeatable) - รันเท่าไหร่ก็ได้โดยไม่เหนื่อย
- **แม่นยำ** (Accurate) - ไม่มี human error
- **ทำงานได้ตลอดเวลา** (24/7) - รันตอนกลางคืนได้
- **ประหยัดต้นทุนในระยะยาว** (Cost-effective long-term) - หลังจาก initial investment
- **Coverage สูง** (High coverage) - ทดสอบได้มากกว่า manual
- **CI/CD Integration** - รันอัตโนมัติทุกครั้งที่ commit code

**ข้อเสีย (Disadvantages):**

- **ต้นทุนเริ่มต้นสูง** (High initial cost) - เขียน automation scripts ใช้เวลา
- 🔧 **ต้อง maintain** (Maintenance) - code เปลี่ยน → tests ต้องเปลี่ยนตาม
- **ต้องมีทักษะ programming** (Programming skills needed) - ไม่ใช่ทุกคนทำได้
- **ไม่เหมาะกับทุกอย่าง** (Not suitable for everything) - บาง tests ทำมือดีกว่า
- **False positives/negatives** - tests อาจล้มเหลวโดยไม่มี bug ที่แท้จริง หรือ pass ทั้งที่มี bug

### 1.2 When to Automate vs Manual Testing - เมื่อไหร่ควร Automate

#### ควร Automate เมื่อ (Should automate):

1. **Regression Testing** - ทดสอบซ้ำๆ บ่อยๆ (Repeated often)
2. **Smoke Testing** - ทดสอบ basic functionality ก่อน release (Before each release)
3. **Data-Driven Testing** - ทดสอบด้วยข้อมูลจำนวนมาก (Large datasets)
4. **Performance Testing** - simulate thousands of users (Load testing)
5. **API Testing** - fast, stable, good ROI
6. **Cross-Browser/Cross-Platform Testing** - ทดสอบหลาย browsers/OS (Multiple browsers)
7. **Stable Features** - features ที่เสถียร ไม่ค่อยมีการเปลี่ยนแปลง (Mature features)

#### 👤 ควรทำ Manual Testing เมื่อ (Should do manual):

1. **Exploratory Testing** - ค้นหา unexpected bugs (Search for unexpected issues)
2. **Usability Testing** - ทดสอบ user experience (User experience)
3. **Ad-hoc Testing** - ทดสอบแบบไม่มี script (Unplanned testing)
4. **New Features** - features ที่ยังไม่ stable (Unstable features)
5. **Complex Scenarios** - ทดสอบที่ยากต่อการ automate (Complex scenarios)
6. **Visual Testing** - ตรวจตรา layout, สี, fonts ด้วยตาเปล่า (Visual verification)
7. **One-time Tests** - ทดสอบครั้งเดียว ไม่ต้องซ้ำ (One-time only)

### 1.3 Test Automation ROI (Return on Investment) - ผลตอบแทนจากการลงทุน

**สูตรคำนวณ ROI:**

```
ROI = (Cost Saved - Cost of Automation) / Cost of Automation × 100%

Where:
Cost Saved = Manual Test Time × Hourly Rate × Number of Executions
Cost of Automation = Development Time × Hourly Rate + Tool Cost + Maintenance Cost
```

**ตัวอย่างการคำนวณ:**

```
Manual Testing (ต้นทุนการทดสอบด้วยมือ):
- Time per execution: 4 hours
- Tester hourly rate: 500 THB
- Executions per year: 100 times
- Annual cost = 4 × 500 × 100 = 200,000 THB

Automated Testing (ต้นทุนการทดสอบแบบอัตโนมัติ):
- Development time: 40 hours
- Developer hourly rate: 800 THB
- Tool cost: 0 THB (free tools)
- Initial cost = 40 × 800 = 32,000 THB
- Maintenance: 10 hours/year × 800 = 8,000 THB
- Execution time: 0.5 hour × 500 × 100 = 25,000 THB
- Annual cost (Year 1) = 32,000 + 8,000 + 25,000 = 65,000 THB
- Annual cost (Year 2+) = 8,000 + 25,000 = 33,000 THB

ROI (Year 1) = (200,000 - 65,000) / 32,000 × 100% = 422%
Break-even point ≈ 3 เดือน
ROI (Year 2+) = (200,000 - 33,000) / 8,000 × 100% = 2,088%
```

**Break-even point** = ประมาณ 3-4 เดือน

**📊 Rule of Thumb (กฎง่ายๆ):**

- Automate ถ้า test จะรันมากกว่า 10-15 ครั้ง
- ROI เด่นชัดหลัง 6-12 เดือน

---

## ส่วนที่ 2: Test Automation Pyramid - ปิรามิดการทดสอบแบบอัตโนมัติ

### 2.1 Test Automation Pyramid - ปิรามิด

```
            /\
           /  \
          /E2E \          👥 UI/E2E Tests (10%) - ทดสอบ End-to-End
         /      \          - ช้า, แพง, เสี่ยง (brittle = พังง่าย)
        /--------\         - ทดสอบ workflow ของผู้ใช้ทั้งหมด
       /          \        - ตัวอย่าง: Playwright, Cypress
      / Integration\
     /    Tests     \      🔗 Integration Tests (20%) - ทดสอบเชื่อมโยงระหว่างหลายส่วน
    /                \     - ความเร็วปานกลาง, ต้นทุนปานกลาง
   /------------------\    - ทดสอบว่า modules ทำงานร่วมกันได้ถูกต้อง
  /                    \   - ตัวอย่าง: API tests, Database tests
 /    Unit Tests        \
/________________________\ ⚙️ Unit Tests (70%) - ทดสอบหน่วยเดียว
                            - เร็ว, ถูก, เสถียร
                            - ทดสอบฟังก์ชันแต่ละตัวอย่างโดยแยก

                            - ตัวอย่าง: Jest unit tests
```

**หลักการ (Principles):**

1. **มาก → น้อย** (Quantity): Unit > Integration > E2E
2. **เร็ว → ช้า** (Speed): Unit > Integration > E2E
3. **ถูก → แพง** (Cost): Unit > Integration > E2E
4. **Stable (เสถียร) → Brittle (พังง่าย)** (Reliability): Unit > Integration > E2E

**ตัวอย่างจาก Library System:**

```javascript
// UNIT TEST (70%) - Test individual functions
test("calculateDueDate returns date 14 days from now", () => {
  const today = new Date("2024-01-01");
  const dueDate = calculateDueDate(today);
  expect(dueDate).toEqual(new Date("2024-01-15"));
});

// INTEGRATION TEST (20%) - Test API endpoints
test("POST /api/books creates book in database", async () => {
  const response = await request(app)
    .post("/api/books")
    .send({ title: "Test Book", author: "Test Author" });
  expect(response.status).toBe(201);

  // Verify in database
  const book = await db.books.findById(response.body.id);
  expect(book.title).toBe("Test Book");
});

// E2E TEST (10%) - Test complete user workflow
test("user can borrow and return a book", async ({ page }) => {
  await page.goto("/login");
  await page.fill("#username", "testuser");
  await page.fill("#password", "password");
  await page.click("#login-button");

  await page.goto("/books");
  await page.click(".book-item:first-child .borrow-button");
  await expect(page.locator(".success-message")).toBeVisible();

  await page.goto("/my-books");
  await page.click(".borrowed-book:first-child .return-button");
  await expect(page.locator(".success-message")).toBeVisible();
});
```

### 2.2 Test Automation Anti-Patterns - รูปแบบที่ผิด 🚫

#### 1️⃣ **Ice Cream Cone Anti-Pattern - รูปแบบการทดสอบที่ผิด (ไอศกรีม)**

```
            /\
           /  \
          / UI \           มาก - E2E Tests (60%)
         /      \          น้อย - Integration (20%)
        /--------\         น้อยมาก - Unit Tests (20%)
       /          \
      /Integration \       Problem: Slow, brittle, expensive
     /    Tests     \
    /                \
   /     Unit         \
  /      Tests         \
 /______________________\
```

**ปัญหา (Problems):**

- Test suite ทำงานช้ามาก (Very slow execution) - รอเวลานานมาก
- แก้ไข UI นิดเดียว → tests พังเยอะ (Fragile tests) - ทดสอบพังง่ายเกินไป
- Debugging ยาก (Hard to debug)
- Maintenance cost สูง (High maintenance)

#### 2️⃣ **Inverted Pyramid Anti-Pattern - ปิรามิดกลับหัว**

```
            /\
           /  \
          /    \
         /      \
        /--------\
       /          \       Integration Tests (50%)
      /            \      E2E Tests (40%)
     /              \     Unit Tests (10%)
    /                \
   /     ไม่มี Unit     \   Problem: Missing foundation
  /____________________\
```

**ปัญหา (Problems):**

- ไม่มี fast feedback loop (No quick feedback)
- Bug ตรวจพบช้า (Late bug detection)
- ไม่รู้ว่า logic แต่ละส่วนถูกต้องหรือไม่ (Unclear logic validation)

#### 3️⃣ **Cupcake Anti-Pattern - รูปแบบคัพเค้ก**

```
     🧁 E2E Only

     No Integration Tests
     No Unit Tests
```

**ปัญหา (Problems):**

- ทดสอบแค่เส้นทางปกติ happy paths ผ่าน UI (Only happy paths) - ไม่ทดสอบความผิดพลาด
- ไม่ได้ test edge cases (พวกเคส ขอบ/พิเศษ) - ไม่มีการทดสอบสถานการณ์นอกเหนือความคาดหมาย
- ช้า และ พังง่ายมาก (Very slow and brittle)

### 2.3 Testing Trophy (Alternative to Pyramid) - ถ้วยการทดสอบ

```
         🏆
        /   \
       / E2E \            🎯 E2E (ชั้นเล็กน้อย - Small layer)
      /-------\
     /         \
    /Integration\        ⭐ Integration (ใหญ่ที่สุด - Largest!)
   /    Tests    \          - ผลตอบแทนดีที่สุด (Best ROI)
  /               \         - Test realistic scenarios
  \   Unit Tests  /      Unit (Solid foundation)
   \             /
    \___________/         💭 Static (Linting, TypeScript)
      Static
```

**Kent C. Dodds** แนะนำ: **เน้น Integration Tests** เพราะให้ confidence สูงสุดต่อ cost

---

## ส่วนที่ 3: UI Automation with Playwright - การทดสอบ UI ด้วย Playwright

### 3.1 Why Playwright? - ทำไมเลือก Playwright

**Playwright** = Modern browser automation framework by Microsoft

**ข้อดี (Advantages):**

- รองรับหลาย browsers (Chromium, Firefox, WebKit) - Multi-browser
- ⚡ เร็วกว่า Selenium - Faster
- 🎯 Auto-wait mechanisms (ไม่ต้อง sleep) - Smart waiting
- 📱 รองรับ mobile testing - Mobile support
- 🎥 Record videos และ screenshots - Video/screenshot recording
- 🔧 Developer-friendly API - Easy to use
- 📦 Built-in test runner - Integrated testing

**เปรียบเทียบ (Comparison):**

| Feature        | Playwright              | Selenium | Cypress         |
| -------------- | ----------------------- | -------- | --------------- |
| Speed          | Fast                    | Slow     | Fast            |
| Browsers       | Chrome, Firefox, Safari | All      | Chrome, Firefox |
| Auto-wait      | Yes                     | No       | Yes             |
| Parallel       | Yes                     | Yes      | No (paid)       |
| Mobile         | Yes                     | Limited  | No              |
| Learning Curve | Easy                    | Hard     | Easy            |

### 3.2 Playwright Basics - พื้นฐาน Playwright

#### Installation - ติดตั้ง

```bash
npm install --save-dev @playwright/test
npx playwright install  # Download browsers
```

#### Basic Test Structure - โครงสร้างพื้นฐาน

```javascript
const { test, expect } = require("@playwright/test");

test("basic test", async ({ page }) => {
  // Navigate
  await page.goto("https://example.com");

  // Interact
  await page.fill("#username", "testuser");
  await page.click("#submit-button");

  // Assert
  await expect(page.locator(".welcome")).toBeVisible();
});
```

### 3.3 Locator Strategies (Best to Worst) - วิธีค้นหา Elements

```javascript
// 1. BEST: data-testid (most stable)
await page.locator('[data-testid="login-button"]').click();

// 2. GOOD: Role + Name (สอดคล้องกับมาตรฐาน accessibility)
await page.getByRole("button", { name: "Login" }).click();

// 3. GOOD: Label text (สร้างตามมาตรฐาน semantic HTML)
await page.getByLabel("Username").fill("testuser");

// 4. OK: Placeholder
await page.getByPlaceholder("Enter username").fill("testuser");

// 5. OK: Text content
await page.getByText("Submit").click();

// 6. AVOID: CSS selectors (พังง่าย - brittle)
await page.locator(".btn.btn-primary.login-btn").click();

// 7. NEVER: XPath (พังง่ายมาก - very brittle)
await page.locator('//div[@class="container"]/button[1]').click();
```

**Best Practice:** ให้ developer เพิ่ม `data-testid` ให้กับ elements

```html
<!-- HTML -->
<button data-testid="login-button" class="btn btn-primary">Login</button>
```

### 3.4 Common Playwright Actions - การกระทำทั่วไป

```javascript
// Navigation
await page.goto("https://example.com");
await page.goBack();
await page.reload();

// Clicking
await page.click("#submit");
await page.dblclick("#item");
await page.locator("#item").click({ force: true }); // Force click

// Typing
await page.fill("#username", "testuser"); // ลบและพิมพ์พร้อมกัน (เร็ว)
await page.type("#username", "testuser"); // พิมพ์ทีละตัวอักษร (ช้า)
await page.press("#input", "Enter");

// Dropdowns
await page.selectOption("#country", "Thailand");
await page.selectOption("#country", { value: "TH" });

// Checkboxes
await page.check("#terms");
await page.uncheck("#newsletter");

// File uploads
await page.setInputFiles("#upload", "path/to/file.pdf");

// Waiting
await page.waitForSelector(".result");
await page.waitForURL("**/dashboard");
await page.waitForLoadState("networkidle");

// Screenshots
await page.screenshot({ path: "screenshot.png" });
await page.locator("#element").screenshot({ path: "element.png" });
```

### 3.5 Assertions - การยืนยัน

```javascript
// Visibility
await expect(page.locator("#element")).toBeVisible();
await expect(page.locator("#element")).toBeHidden();

// Text
await expect(page.locator("#title")).toHaveText("Welcome");
await expect(page.locator("#title")).toContainText("Wel");

// URL
await expect(page).toHaveURL("https://example.com/dashboard");
await expect(page).toHaveURL(/dashboard/);

// Count
await expect(page.locator(".item")).toHaveCount(5);

// Value
await expect(page.locator("#input")).toHaveValue("test");

// Attribute
await expect(page.locator("#link")).toHaveAttribute("href", "/about");

// CSS
await expect(page.locator("#element")).toHaveCSS("color", "rgb(255, 0, 0)");
```

### 3.6 Page Object Model (POM) Pattern - รูปแบบ Page Object

**ปัญหาของการเขียน tests โดยตรง (Problems without POM):**

```javascript
// BAD: Duplicate code, hard to maintain
test("test 1", async ({ page }) => {
  await page.goto("http://localhost:3000/login");
  await page.fill("#username", "user1");
  await page.fill("#password", "pass1");
  await page.click("#login-button");
});

test("test 2", async ({ page }) => {
  await page.goto("http://localhost:3000/login");
  await page.fill("#username", "user2");
  await page.fill("#password", "pass2");
  await page.click("#login-button");
});
```

**วิธีแก้: Page Object Model**

```javascript
// pages/LoginPage.js
class LoginPage {
  constructor(page) {
    this.page = page;

    // Locators
    this.usernameInput = page.locator('[data-testid="username"]');
    this.passwordInput = page.locator('[data-testid="password"]');
    this.loginButton = page.locator('[data-testid="login-button"]');
    this.errorMessage = page.locator('[data-testid="error-message"]');
  }

  async goto() {
    await this.page.goto("http://localhost:3000/login");
  }

  async login(username, password) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    await this.loginButton.click();
  }

  async getErrorMessage() {
    return await this.errorMessage.textContent();
  }
}

module.exports = { LoginPage };
```

```javascript
// tests/login.spec.js
const { test, expect } = require("@playwright/test");
const { LoginPage } = require("../pages/LoginPage");

test("successful login", async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login("validuser", "validpass");
  await expect(page).toHaveURL(/dashboard/);
});

test("invalid credentials", async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login("invalid", "invalid");

  const error = await loginPage.getErrorMessage();
  expect(error).toContain("Invalid credentials");
});
```

**ข้อดีของ POM (Benefits):**

- **Reusability** (ใช้ซ้ำได้) - ใช้ซ้ำได้หลายที่
- **Maintainability** (ง่ายต่อการแก้ไข) - แก้ไขที่เดียว
- **Readability** (อ่านง่าย) - tests อ่านง่ายขึ้น
- **Separation of Concerns** - แยก UI logic ออก

---

## ส่วนที่ 4: API Automation with Supertest - ทดสอบ API ด้วย Supertest

### 4.1 Why API Testing? - ทำไมเลือก API Testing

**API Testing** = ทดสอบ API endpoints โดยตรง (ไม่ผ่าน UI)

**ข้อดี (Advantages):**

- **เร็วมาก** (Very fast) - 100x เร็วกว่า UI tests
- **Stable** - ไม่พึ่ง UI ที่เปลี่ยนบ่อย
- **Precise** - รู้ชัดว่า bug อยู่ที่ API layer
- **Easy to automate** - REST API = predictable
- **Better coverage** - test edge cases ได้ง่าย
- **ROI สูง** - write once, run forever

### 4.2 Supertest Basics - พื้นฐาน Supertest

**Supertest** = Library สำหรับทดสอบ HTTP servers

```bash
npm install --save-dev supertest
```

#### Basic API Test - การทดสอบพื้นฐาน

```javascript
const request = require("supertest");
const app = require("../app"); // Your Express app

describe("Book API", () => {
  test("GET /api/books returns list of books", async () => {
    const response = await request(app).get("/api/books");

    expect(response.status).toBe(200);
    expect(response.body).toBeInstanceOf(Array);
    expect(response.body.length).toBeGreaterThan(0);
  });
});
```

### 4.3 Testing CRUD Operations - ทดสอบ CRUD

```javascript
describe("Book API CRUD", () => {
  let bookId;

  // CREATE
  test("POST /api/books creates new book", async () => {
    const newBook = {
      title: "Clean Code",
      author: "Robert Martin",
      isbn: "978-0132350884",
      publishedYear: 2008,
    };

    const response = await request(app)
      .post("/api/books")
      .send(newBook)
      .set("Content-Type", "application/json");

    expect(response.status).toBe(201);
    expect(response.body).toHaveProperty("id");
    expect(response.body.title).toBe(newBook.title);

    bookId = response.body.id;
  });

  // READ
  test("GET /api/books/:id returns book details", async () => {
    const response = await request(app).get(`/api/books/${bookId}`);

    expect(response.status).toBe(200);
    expect(response.body.id).toBe(bookId);
    expect(response.body.title).toBe("Clean Code");
  });

  // UPDATE
  test("PUT /api/books/:id updates book", async () => {
    const updates = { title: "Clean Code - Updated" };

    const response = await request(app)
      .put(`/api/books/${bookId}`)
      .send(updates);

    expect(response.status).toBe(200);
    expect(response.body.title).toBe("Clean Code - Updated");
  });

  // DELETE
  test("DELETE /api/books/:id deletes book", async () => {
    const response = await request(app).delete(`/api/books/${bookId}`);
    expect(response.status).toBe(204);

    const getResponse = await request(app).get(`/api/books/${bookId}`);
    expect(getResponse.status).toBe(404);
  });
});
```

### 4.4 Testing with Authentication - ทดสอบด้วยการรับรอง

```javascript
describe("Protected API Endpoints", () => {
  let authToken;

  beforeAll(async () => {
    const response = await request(app)
      .post("/api/auth/login")
      .send({ username: "testuser", password: "password123" });

    authToken = response.body.token;
  });

  test("GET /api/admin/users requires authentication", async () => {
    // Without token - should fail
    const response1 = await request(app).get("/api/admin/users");
    expect(response1.status).toBe(401);

    // With token - should succeed
    const response2 = await request(app)
      .get("/api/admin/users")
      .set("Authorization", `Bearer ${authToken}`);

    expect(response2.status).toBe(200);
  });
});
```

### 4.5 Testing Error Handling - ทดสอบการจัดการข้อผิดพลาด

```javascript
describe("API Error Handling", () => {
  test("POST /api/books with missing fields returns 400", async () => {
    const invalidBook = { title: "Test" }; // Missing author

    const response = await request(app).post("/api/books").send(invalidBook);

    expect(response.status).toBe(400);
    expect(response.body).toHaveProperty("error");
    expect(response.body.error).toContain("author");
  });

  test("GET /api/books/:id with invalid ID returns 404", async () => {
    const response = await request(app).get("/api/books/99999");

    expect(response.status).toBe(404);
    expect(response.body.error).toContain("not found");
  });

  test("POST /api/books with duplicate ISBN returns 409", async () => {
    const book = {
      title: "Test Book",
      author: "Test Author",
      isbn: "978-0132350884",
    };

    await request(app).post("/api/books").send(book);

    const response = await request(app).post("/api/books").send(book);
    expect(response.status).toBe(409);
  });
});
```

---

## ส่วนที่ 5: Test Automation Framework Design - ออกแบบ Framework

### 5.1 Framework Structure - โครงสร้าง

```
library-testing/
├── tests/
│   ├── unit/              # Unit tests
│   ├── integration/       # API tests
│   └── e2e/              # Playwright tests
│       ├── pages/        # Page Objects
│       │   ├── LoginPage.js
│       │   ├── BookPage.js
│       │   └── BasePage.js
│       ├── fixtures/     # Test data
│       │   └── users.json
│       ├── specs/        # Test specs
│       │   ├── login.spec.js
│       │   └── books.spec.js
│       └── utils/        # Helper functions
│           └── testHelpers.js
├── playwright.config.js   # Playwright config
├── jest.config.js        # Jest config
└── package.json
```

### 5.2 Configuration Management - จัดการคอนฟิก

```javascript
// config/test.config.js
module.exports = {
  baseURL: process.env.BASE_URL || "http://localhost:3000",
  apiURL: process.env.API_URL || "http://localhost:3000/api",
  timeout: 30000,
  retries: 2,

  users: {
    admin: {
      username: "admin",
      password: "admin123",
    },
    member: {
      username: "member",
      password: "member123",
    },
  },
};
```

### 5.3 Base Page Class - คลาส Base

```javascript
// tests/e2e/pages/BasePage.js
class BasePage {
  constructor(page) {
    this.page = page;
  }

  async goto(path) {
    await this.page.goto(`http://localhost:3000${path}`);
  }

  async waitForPageLoad() {
    await this.page.waitForLoadState("networkidle");
  }

  async takeScreenshot(name) {
    await this.page.screenshot({
      path: `screenshots/${name}.png`,
      fullPage: true,
    });
  }

  async getErrorMessage() {
    const errorElement = this.page.locator('[data-testid="error-message"]');
    if (await errorElement.isVisible()) {
      return await errorElement.textContent();
    }
    return null;
  }
}

module.exports = { BasePage };
```

### 5.4 Test Helpers - ตัวช่วยทดสอบ

```javascript
// tests/e2e/utils/testHelpers.js
const { faker } = require("@faker-js/faker");

class TestHelpers {
  static generateUser() {
    return {
      username: faker.internet.userName(),
      email: faker.internet.email(),
      password: faker.internet.password(),
      firstName: faker.person.firstName(),
      lastName: faker.person.lastName(),
    };
  }

  static generateBook() {
    return {
      title: faker.lorem.words(3),
      author: faker.person.fullName(),
      isbn: faker.string.numeric(13),
      publishedYear: faker.number.int({ min: 1900, max: 2024 }),
    };
  }

  static async loginAsAdmin(page) {
    const loginPage = new LoginPage(page);
    await loginPage.goto();
    await loginPage.login("admin", "admin123");
  }
}

module.exports = { TestHelpers };
```

### 5.5 Best Practices - แนวปฏิบัติที่ดี

#### 1. **DRY (Don't Repeat Yourself) - อย่าซ้ำซ้อน**

```javascript
// BAD
test("test 1", async () => {
  await page.goto("http://localhost:3000/login");
  await page.fill("#username", "user");
});

// GOOD
beforeEach(async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
});
```

#### 2. **Independent Tests - ทดสอบแยกอิสระ**

```javascript
// BAD: Tests depend on each other
test('create book', async () => {
  bookId = /* create book */;
});

// GOOD: Each test is independent
test('update book', async () => {
  const bookId = await createTestBook();
  // Test
  await deleteBook(bookId);
});
```

#### 3. **Explicit Waits - รอให้ชัดเจน (ไม่ใช่การรอโดยเดา)**

```javascript
// BAD - รอตัวเลขเสยวหน้า (อาจเป็นการรอโดยตัวเลข)
await page.click("#submit");
await page.waitForTimeout(2000); // รอ 2 วินาที (อาจสั้นเกิน หรือนอย)

// GOOD - รอจนกว่า element มาปรากฏ
await page.click("#submit");
await page.waitForSelector(".success-message"); // รอทำความชัธอีบีได้ที element มาปรากฏ
```

#### 4. **Descriptive Test Names - ชื่อทดสอบที่ชัดเจน**

```javascript
// BAD - ชื่อไม่ชัดเจน ไม่รู้ว่าทดสอบอะไร
test("test 1", async () => {});
test("test 2", async () => {});

// GOOD - ชื่อบ่งบอกว่า test ทำอะไร
test("user with valid credentials can login successfully", async () => {});
test("user cannot login with invalid credentials", async () => {});
```

---

## ส่วนที่ 6: Mobile Testing Overview - ภาพรวม Mobile Testing

### 6.1 Mobile Testing Approaches - วิธี Mobile Testing

**1. Responsive Testing (ทดสอบผ่าน Desktop browser)**

```javascript
test("mobile responsive design", async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 667 }); // iPhone SE
  await page.goto("http://localhost:3000");

  await expect(page.locator(".mobile-menu")).toBeVisible();
});
```

**2. Mobile Browser Testing (Playwright)**

```javascript
// playwright.config.js
const devices = require("@playwright/test").devices;

module.exports = {
  projects: [
    {
      name: "Mobile Chrome",
      use: { ...devices["Pixel 5"] },
    },
    {
      name: "Mobile Safari",
      use: { ...devices["iPhone 13"] },
    },
  ],
};
```

**3. Native Mobile Apps (Appium)**

- **Appium** = WebDriver for mobile apps
- ทดสอบ native iOS/Android apps
- ต้องการ: Real devices หรือ emulators
- Learning curve สูง

**เมื่อไหร่ต้องทำ Mobile Testing (When needed):**

- เว็บไซต์มี responsive design
- มี mobile users เยอะ
- มี native mobile app
- Touch gestures (swipe, pinch)

---

## 🎓 Summary & Key Takeaways - สรุปและบทเรียนหลัก

#### ควร Automate (ควรทำการทดสอบแบบอัตโนมัติ):

- **Regression tests** - ทดสอบซ้ำบ่อยๆ (เมื่อ code เปลี่ยน ต้องแน่ใจว่าไม่มี bug ใหม่)
- **Smoke tests** - ทดสอบพื้นฐานก่อน deploy ว่า feature หลักทำงาน
- **API tests** - เร็ว, เสถียร, ROI สูง
- **Data-driven tests** - ทดสอบด้วยข้อมูลจำนวนมาก

### 👤 ควรทำ Manual (ควรทำการทดสอบด้วยมือ):

- **Exploratory testing** - ค้นหา bug ที่ไม่คาดหมาย
- **Usability testing** - ทดสอบประสบการณ์ผู้ใช้ ว่าใช้งานง่ายหรือไม่
- **New features** - features ใหม่ที่ยังไม่เสถียร
- **One-time tests** - ทดสอบครั้งเดียว ไม่ต้องซ้ำ

### 🏗️ Test Automation Pyramid:

- **70% Unit Tests** - เร็ว, ถูก, stable
- **20% Integration Tests** - ROI สูง
- **10% E2E Tests** - ครอบคลุม user workflows

### 🎨 Page Object Model:

- แยก UI logic ออกจาก test logic
- Reusable, maintainable, readable

### 🔌 API Testing:

- เร็วกว่า UI testing 100x
- Stable และ precise
- ROI สูง

---
