# 📋 API Testing Template cho Fresher Tester

> **Dành cho ai?** Bạn mới bắt đầu học API Testing và cần một template chuẩn để thực hành.
> **Mục tiêu:** Hiểu rõ 5 loại request API phổ biến nhất và biết cần kiểm tra gì cho từng loại.

---

## 🗂️ Mục Lục

- [Khái niệm cơ bản cần biết](#-khái-niệm-cơ-bản-cần-biết)
- [1. GET List - Lấy danh sách](#1-get-list---lấy-danh-sách)
- [2. GET Detail - Lấy chi tiết 1 item](#2-get-detail---lấy-chi-tiết-1-item)
- [3. POST Create - Tạo mới](#3-post-create---tạo-mới)
- [4. PUT Update - Cập nhật](#4-put-update---cập-nhật)
- [5. DELETE - Xóa](#5-delete---xóa)
- [Bảng HTTP Status Code hay gặp](#-bảng-http-status-code-hay-gặp)
- [Checklist tổng hợp](#-checklist-tổng-hợp)
- [Tips cho Fresher](#-tips-cho-fresher)

---

## 💡 Khái niệm cơ bản cần biết

### HTTP Method là gì?
```
GET    → Lấy dữ liệu (chỉ đọc, không thay đổi gì trên server)
POST   → Tạo mới dữ liệu
PUT    → Cập nhật toàn bộ dữ liệu (ghi đè hoàn toàn)
PATCH  → Cập nhật một phần dữ liệu (chỉ thay đổi field được gửi lên)
DELETE → Xóa dữ liệu
```

### Cấu trúc một API Request gồm:
```
┌─────────────────────────────────────────────┐
│  METHOD  │  URL (Endpoint)                  │
├─────────────────────────────────────────────┤
│  HEADERS │  Thông tin meta (token, loại data)│
├─────────────────────────────────────────────┤
│  BODY    │  Dữ liệu gửi lên (POST/PUT/PATCH) │
└─────────────────────────────────────────────┘
```

### Ví dụ thực tế để dễ hình dung:
```
Tưởng tượng bạn đang dùng app quản lý sản phẩm:

GET    /products          → Xem toàn bộ danh sách sản phẩm
GET    /products/5        → Xem chi tiết sản phẩm có ID = 5
POST   /products          → Thêm sản phẩm mới vào danh sách
PUT    /products/5        → Sửa toàn bộ thông tin sản phẩm ID = 5
DELETE /products/5        → Xóa sản phẩm ID = 5
```

---

## 1. GET List - Lấy danh sách

> **Mục đích:** Lấy về một danh sách nhiều items. Ví dụ: danh sách users, danh sách sản phẩm...

### 📌 Thông tin Request

| Thành phần | Giá trị |
|-----------|---------|
| **Method** | `GET` |
| **URL Pattern** | `https://api.example.com/v1/{resource}` |
| **URL Ví dụ** | `https://api.example.com/v1/products` |

### 🔑 Headers

```
Content-Type  : application/json
Authorization : Bearer {your_access_token}
Accept        : application/json
```

> **Giải thích Headers:**
> - `Content-Type`: Nói với server "Tôi đang gửi dữ liệu dạng JSON"
> - `Authorization`: Token xác thực - chứng minh bạn có quyền gọi API này
> - `Accept`: Nói với server "Tôi muốn nhận dữ liệu về dạng JSON"

### 📦 Request Body

```
Không có Body! 
GET request không gửi body lên server.
Nếu cần filter/search, dùng Query Parameters trên URL.
```

### 🔍 Query Parameters (Tùy chọn)

```
?page=1          → Trang số mấy (phân trang)
?limit=10        → Mỗi trang bao nhiêu items
?sort=name       → Sắp xếp theo field nào
?order=asc       → Thứ tự tăng dần (asc) hay giảm dần (desc)
?search=iphone   → Tìm kiếm theo từ khóa
?status=active   → Lọc theo trạng thái

Ví dụ URL đầy đủ:
GET https://api.example.com/v1/products?page=1&limit=10&sort=name&order=asc
```

### ✅ Expected Response

**Status Code:** `200 OK`

```json
{
  "status": "success",
  "message": "Lấy danh sách thành công",
  "data": {
    "items": [
      {
        "id": 1,
        "name": "iPhone 15 Pro",
        "price": 29990000,
        "category": "smartphone",
        "status": "active",
        "created_at": "2024-01-15T08:30:00Z"
      },
      {
        "id": 2,
        "name": "Samsung Galaxy S24",
        "price": 22990000,
        "category": "smartphone",
        "status": "active",
        "created_at": "2024-01-16T10:00:00Z"
      }
    ],
    "pagination": {
      "current_page": 1,
      "total_pages": 5,
      "total_items": 48,
      "items_per_page": 10
    }
  }
}
```

### 🧪 Test Points cần kiểm tra

#### ✔️ Happy Path (Trường hợp đúng - test trước)
```
□ Status code = 200
□ Response trả về đúng định dạng JSON
□ Field "data.items" là một array (không phải object, không phải string)
□ Mỗi item trong array có đủ các field cần thiết (id, name, price...)
□ Kiểu dữ liệu đúng: id là số, name là string, price là số...
□ Có thông tin phân trang (pagination) nếu API hỗ trợ
□ Số lượng items trả về không vượt quá limit đã set
□ Response time < 2000ms (dưới 2 giây)
```

#### ❌ Unhappy Path (Trường hợp lỗi - test sau)
```
□ Gọi không có token → Expect 401 Unauthorized
□ Gọi với token hết hạn → Expect 401 Unauthorized  
□ Gọi với token không có quyền → Expect 403 Forbidden
□ Truyền page = -1 (số âm) → Expect 400 Bad Request hoặc trả trang 1
□ Truyền limit = 999999 (quá lớn) → Server xử lý an toàn, không crash
□ Truyền sort theo field không tồn tại → Expect 400 hoặc ignore
```

#### 🔲 Edge Cases (Trường hợp biên)
```
□ Khi database rỗng (chưa có data) → Trả về array rỗng [], không phải null
□ Trang cuối cùng có ít items hơn limit → Vẫn trả về đúng
□ Search với ký tự đặc biệt: !@#$%^&* → Không gây lỗi server
□ Search với chuỗi rất dài (1000+ ký tự) → Không gây lỗi server
```

---

## 2. GET Detail - Lấy chi tiết 1 item

> **Mục đích:** Lấy thông tin đầy đủ của MỘT item cụ thể, xác định bằng ID hoặc slug.

### 📌 Thông tin Request

| Thành phần | Giá trị |
|-----------|---------|
| **Method** | `GET` |
| **URL Pattern** | `https://api.example.com/v1/{resource}/{id}` |
| **URL Ví dụ** | `https://api.example.com/v1/products/123` |

> **Lưu ý URL Pattern:**
> - `{resource}` = tên nhóm tài nguyên: products, users, orders...
> - `{id}` = ID của item cụ thể bạn muốn xem: 1, 42, 999...

### 🔑 Headers

```
Content-Type  : application/json
Authorization : Bearer {your_access_token}
Accept        : application/json
```

### 📦 Request Body

```
Không có Body!
ID được truyền trực tiếp trên URL, không cần body.
```

### ✅ Expected Response

**Status Code:** `200 OK`

```json
{
  "status": "success",
  "message": "Lấy chi tiết sản phẩm thành công",
  "data": {
    "id": 123,
    "name": "iPhone 15 Pro Max",
    "slug": "iphone-15-pro-max",
    "description": "Điện thoại cao cấp nhất của Apple năm 2024",
    "price": 34990000,
    "original_price": 38990000,
    "discount_percent": 10,
    "category": {
      "id": 5,
      "name": "Smartphone"
    },
    "images": [
      "https://cdn.example.com/products/iphone15-1.jpg",
      "https://cdn.example.com/products/iphone15-2.jpg"
    ],
    "stock": 50,
    "status": "active",
    "created_at": "2024-01-15T08:30:00Z",
    "updated_at": "2024-03-20T14:00:00Z"
  }
}
```

### 🧪 Test Points cần kiểm tra

#### ✔️ Happy Path
```
□ Status code = 200
□ Response trả về đúng 1 object (không phải array)
□ ID trong response khớp với ID đã request
□ Đầy đủ các field cần thiết (không bị thiếu field)
□ Kiểu dữ liệu đúng cho từng field:
   - id: number
   - name: string  
   - price: number (không phải string "34990000")
   - images: array
   - status: string
□ Giá trị hợp lệ: price > 0, stock >= 0
□ Response time < 1000ms
```

#### ❌ Unhappy Path
```
□ ID không tồn tại (ví dụ: /products/99999) → Expect 404 Not Found
□ ID = 0 hoặc ID âm (/products/0, /products/-1) → Expect 400 hoặc 404
□ ID là chữ thay vì số (/products/abc) → Expect 400 Bad Request
□ ID là khoảng trắng (/products/ ) → Expect 400 hoặc 404
□ Không có token → Expect 401 Unauthorized
□ Token không có quyền xem item này → Expect 403 Forbidden
```

#### 🔲 Edge Cases
```
□ ID rất lớn (9999999999) → Expect 404, không crash server
□ ID có ký tự đặc biệt (/products/1;DROP TABLE) → Expect 400, không SQL Injection
□ Item bị xóa mềm (soft delete) → Expect 404 hoặc có trường is_deleted
```

---

## 3. POST Create - Tạo mới

> **Mục đích:** Gửi dữ liệu lên server để tạo một item mới. Đây là request **có Body** đầu tiên.

### 📌 Thông tin Request

| Thành phần | Giá trị |
|-----------|---------|
| **Method** | `POST` |
| **URL Pattern** | `https://api.example.com/v1/{resource}` |
| **URL Ví dụ** | `https://api.example.com/v1/products` |

> **Lưu ý:** POST URL giống GET List (cùng `/products`), khác nhau ở Method!

### 🔑 Headers

```
Content-Type  : application/json        ← BẮT BUỘC khi có Body
Authorization : Bearer {your_access_token}
Accept        : application/json
```

> ⚠️ **Quan trọng:** Khi gửi Body dạng JSON, **bắt buộc** phải có `Content-Type: application/json`
> Nếu thiếu header này, server có thể không đọc được body bạn gửi lên!

### 📦 Request Body - Đầy đủ (Valid)

```json
{
  "name": "Samsung Galaxy S24 Ultra",
  "description": "Flagship cao cấp nhất của Samsung với bút S Pen",
  "price": 31990000,
  "original_price": 33990000,
  "category_id": 5,
  "stock": 100,
  "images": [
    "https://cdn.example.com/products/s24ultra-1.jpg",
    "https://cdn.example.com/products/s24ultra-2.jpg"
  ],
  "status": "active",
  "tags": ["samsung", "flagship", "android"]
}
```

> **Giải thích từng field:**
> ```
> name         → Tên sản phẩm (bắt buộc, string)
> description  → Mô tả chi tiết (tùy chọn, string)
> price        → Giá bán (bắt buộc, number > 0)
> original_price → Giá gốc trước khi giảm (tùy chọn, number)
> category_id  → ID danh mục sản phẩm thuộc về (bắt buộc, number)
> stock        → Số lượng tồn kho (bắt buộc, number >= 0)
> images       → Mảng URL ảnh (tùy chọn, array of strings)
> status       → Trạng thái: "active" hoặc "inactive" (bắt buộc, string)
> tags         → Nhãn gắn thêm (tùy chọn, array of strings)
> ```

### 📦 Request Body - Các biến thể để test

```json
// Body thiếu field bắt buộc (dùng để test validation)
{
  "description": "Mô tả thôi, thiếu name và price"
}
```

```json
// Body với dữ liệu sai kiểu (dùng để test type validation)
{
  "name": 12345,
  "price": "ba mươi triệu",
  "stock": -50,
  "status": "unknown_status"
}
```

```json
// Body rỗng hoàn toàn (dùng để test)
{}
```

### ✅ Expected Response

**Status Code:** `201 Created`

```json
{
  "status": "success",
  "message": "Tạo sản phẩm thành công",
  "data": {
    "id": 124,
    "name": "Samsung Galaxy S24 Ultra",
    "description": "Flagship cao cấp nhất của Samsung với bút S Pen",
    "price": 31990000,
    "original_price": 33990000,
    "category_id": 5,
    "stock": 100,
    "images": [
      "https://cdn.example.com/products/s24ultra-1.jpg"
    ],
    "status": "active",
    "tags": ["samsung", "flagship", "android"],
    "created_at": "2024-03-21T09:00:00Z",
    "updated_at": "2024-03-21T09:00:00Z"
  }
}
```

> **Chú ý Response 201:**
> - Server tự động thêm: `id`, `created_at`, `updated_at`
> - Dữ liệu trả về phải khớp với dữ liệu bạn vừa gửi lên
> - Từ đây dùng ID = 124 để test GET Detail, PUT, DELETE

### 🧪 Test Points cần kiểm tra

#### ✔️ Happy Path
```
□ Status code = 201 (không phải 200!)
□ Response trả về object của item vừa tạo
□ ID được server tự sinh ra (tồn tại và là số dương)
□ Dữ liệu trả về khớp 100% với dữ liệu đã gửi
□ created_at và updated_at được server tự tạo, không phải null
□ Sau khi tạo, gọi GET Detail với ID mới → phải tìm thấy (verify data tồn tại)
□ Sau khi tạo, gọi GET List → số lượng item tăng thêm 1
```

#### ❌ Unhappy Path - Validation
```
□ Thiếu field bắt buộc "name" → Expect 400 + message nói rõ field nào thiếu
□ Thiếu field bắt buộc "price" → Expect 400
□ Thiếu nhiều field cùng lúc → Expect 400 + liệt kê TẤT CẢ field thiếu
□ name = "" (chuỗi rỗng) → Expect 400 (không được tạo sản phẩm tên trống)
□ price = -1000 (âm) → Expect 400 (giá không được âm)
□ price = 0 → Expect 400 hoặc 422 (tùy business rule)
□ stock = -5 (âm) → Expect 400
□ category_id không tồn tại → Expect 400 hoặc 422
□ status = "deleted" (giá trị không hợp lệ) → Expect 400
□ Gửi body rỗng {} → Expect 400
□ Không gửi body gì → Expect 400
```

#### ❌ Unhappy Path - Authentication
```
□ Không có token → Expect 401
□ Token sai định dạng → Expect 401
□ Token hết hạn → Expect 401
□ Token không có quyền tạo sản phẩm → Expect 403
```

#### 🔲 Edge Cases
```
□ name quá dài (255+ ký tự) → Expect 400 với message độ dài tối đa
□ Tạo 2 sản phẩm trùng tên → Expect 409 Conflict hoặc cho phép tùy business
□ price = 0.001 (số thập phân rất nhỏ) → Server xử lý đúng
□ Gửi thêm field không có trong schema: "hacker_field": "test" → Server ignore hoặc báo lỗi
□ name chứa HTML: "<script>alert('xss')</script>" → Server xử lý an toàn
□ Gọi POST cùng data 2 lần liên tiếp → Tạo 2 records hay 1? (idempotency)
```

---

## 4. PUT Update - Cập nhật

> **Mục đích:** Cập nhật thông tin của một item đã tồn tại. PUT thường **thay thế toàn bộ** dữ liệu.

### 📌 Thông tin Request

| Thành phần | Giá trị |
|-----------|---------|
| **Method** | `PUT` |
| **URL Pattern** | `https://api.example.com/v1/{resource}/{id}` |
| **URL Ví dụ** | `https://api.example.com/v1/products/124` |

> **PUT vs PATCH - Phân biệt quan trọng:**
> ```
> PUT   → Gửi toàn bộ dữ liệu, field không gửi sẽ bị xóa/reset
>          Ví dụ: Chỉ gửi {name: "abc"} → các field khác thành null/default
>
> PATCH → Chỉ gửi field muốn thay đổi, field không gửi giữ nguyên
>          Ví dụ: Chỉ gửi {price: 999} → chỉ price thay đổi, name giữ nguyên
> ```

### 🔑 Headers

```
Content-Type  : application/json
Authorization : Bearer {your_access_token}
Accept        : application/json
```

### 📦 Request Body - Cập nhật đầy đủ (PUT)

```json
{
  "name": "Samsung Galaxy S24 Ultra - Phiên bản Titanium",
  "description": "Flagship cao cấp nhất của Samsung, màu Titanium mới",
  "price": 29990000,
  "original_price": 31990000,
  "category_id": 5,
  "stock": 80,
  "images": [
    "https://cdn.example.com/products/s24ultra-titanium-1.jpg",
    "https://cdn.example.com/products/s24ultra-titanium-2.jpg"
  ],
  "status": "active",
  "tags": ["samsung", "flagship", "android", "titanium"]
}
```

> **Lưu ý PUT:**
> Bạn phải gửi **TẤT CẢ** các field, không chỉ field muốn thay đổi.
> Nếu chỉ gửi `{price: 29990000}` và field `name` không được gửi
> → `name` có thể bị xóa hoặc reset về giá trị mặc định!

### 📦 Request Body - Chỉ cập nhật một phần (PATCH)

```json
{
  "price": 27990000,
  "stock": 60
}
```

### ✅ Expected Response

**Status Code:** `200 OK`

```json
{
  "status": "success",
  "message": "Cập nhật sản phẩm thành công",
  "data": {
    "id": 124,
    "name": "Samsung Galaxy S24 Ultra - Phiên bản Titanium",
    "description": "Flagship cao cấp nhất của Samsung, màu Titanium mới",
    "price": 29990000,
    "original_price": 31990000,
    "category_id": 5,
    "stock": 80,
    "images": [
      "https://cdn.example.com/products/s24ultra-titanium-1.jpg",
      "https://cdn.example.com/products/s24ultra-titanium-2.jpg"
    ],
    "status": "active",
    "tags": ["samsung", "flagship", "android", "titanium"],
    "created_at": "2024-03-21T09:00:00Z",
    "updated_at": "2024-03-21T15:30:00Z"
  }
}
```

> **Chú ý Response 200:**
> - `created_at` **KHÔNG THAY ĐỔI** - vẫn là thời điểm tạo ban đầu
> - `updated_at` **ĐÃ THAY ĐỔI** - cập nhật thành thời điểm vừa sửa
> - Dữ liệu trả về phải phản ánh thay đổi mới nhất

### 🧪 Test Points cần kiểm tra

#### ✔️ Happy Path
```
□ Status code = 200
□ Dữ liệu trả về khớp với dữ liệu vừa cập nhật
□ updated_at đã thay đổi sang thời gian mới
□ created_at KHÔNG THAY ĐỔI (quan trọng!)
□ ID không thay đổi (server không tạo ID mới)
□ Sau khi update, gọi GET Detail → phải thấy dữ liệu mới
□ Các field không thay đổi vẫn giữ nguyên giá trị cũ (với PATCH)
```

#### ❌ Unhappy Path
```
□ ID không tồn tại trong URL → Expect 404 Not Found
□ Thiếu field bắt buộc trong body → Expect 400
□ Cập nhật name = "" (rỗng) → Expect 400
□ Cập nhật price âm → Expect 400
□ category_id không tồn tại → Expect 400 hoặc 422
□ Không có token → Expect 401
□ Token không có quyền cập nhật → Expect 403
□ Cố cập nhật item của người khác → Expect 403
```

#### 🔲 Edge Cases
```
□ Gửi PUT với body giống hệt data cũ → Expect 200, không lỗi
□ updated_at có thực sự thay đổi khi data không đổi? (tùy team quy định)
□ Cập nhật status từ "active" → "inactive" → GET List không còn thấy item này
□ Gửi đúng ID trong URL nhưng ID trong body khác → Server dùng ID nào?
□ Cập nhật nhiều lần liên tiếp → Chỉ giữ version mới nhất
```

---

## 5. DELETE - Xóa

> **Mục đích:** Xóa một item khỏi hệ thống. Có 2 loại xóa cần phân biệt.

### 📌 Thông tin Request

| Thành phần | Giá trị |
|-----------|---------|
| **Method** | `DELETE` |
| **URL Pattern** | `https://api.example.com/v1/{resource}/{id}` |
| **URL Ví dụ** | `https://api.example.com/v1/products/124` |

> **2 loại DELETE cần biết:**
> ```
> Hard Delete (Xóa cứng):
> → Xóa hoàn toàn khỏi database
> → Không thể khôi phục
> → Gọi GET Detail sau → 404
>
> Soft Delete (Xóa mềm):
> → Chỉ đánh dấu is_deleted = true (hoặc status = deleted)
> → Vẫn còn trong database, có thể khôi phục
> → Gọi GET Detail sau → 404 hoặc trả về với is_deleted = true
> → Thường dùng trong production để an toàn
> ```

### 🔑 Headers

```
Content-Type  : application/json
Authorization : Bearer {your_access_token}
Accept        : application/json
```

### 📦 Request Body

```
Thường KHÔNG có body.
ID được truyền trực tiếp trên URL.

Một số API cho phép gửi body để xóa nhiều items cùng lúc:
{
  "ids": [124, 125, 126]
}
Nhưng đây là trường hợp đặc biệt, không phải chuẩn.
```

### ✅ Expected Response

**Trường hợp 1 - Trả về item vừa xóa:** `200 OK`

```json
{
  "status": "success",
  "message": "Xóa sản phẩm thành công",
  "data": {
    "id": 124,
    "name": "Samsung Galaxy S24 Ultra - Phiên bản Titanium",
    "deleted_at": "2024-03-21T16:00:00Z"
  }
}
```

**Trường hợp 2 - Không trả về gì cả:** `204 No Content`

```
Status: 204
Body: (trống hoàn toàn)
```

> **Giải thích 200 vs 204:**
> - `200 OK` + trả về data: Bạn biết item nào vừa bị xóa
> - `204 No Content` + không có body: Đơn giản hơn, chỉ xác nhận "đã xóa xong"
> - Cả hai đều đúng, tùy team chọn convention

### 🧪 Test Points cần kiểm tra

#### ✔️ Happy Path
```
□ Status code = 200 hoặc 204 (tùy API)
□ Nếu 200: Response có message xác nhận xóa thành công
□ Nếu 204: Response body PHẢI TRỐNG (không có gì)
□ Sau khi xóa, gọi GET Detail với ID đó → Phải nhận 404
□ Sau khi xóa, gọi GET List → Số lượng item giảm đi 1
□ Sau khi xóa, gọi PUT/PATCH với ID đó → Phải nhận 404 (không update được)
```

#### ❌ Unhappy Path
```
□ ID không tồn tại → Expect 404 Not Found
□ Xóa item đã bị xóa trước đó (double delete) → Expect 404 (không phải 200!)
□ ID = 0 hoặc âm → Expect 400 hoặc 404
□ ID là chữ → Expect 400 Bad Request
□ Không có token → Expect 401
□ Token không có quyền xóa → Expect 403
□ Xóa item đang được tham chiếu bởi item khác → Expect 409 Conflict hoặc 422
   Ví dụ: Xóa Category khi vẫn còn Product thuộc Category đó
```

#### 🔲 Edge Cases
```
□ Xóa item vừa mới tạo trong test → Đảm bảo dữ liệu test không ảnh hưởng production
□ Xóa xong gọi lại GET → Phải 404, không phải 500 (server không crash)
□ Xóa item xong xóa tiếp cùng ID → 404 (idempotent check)
□ Soft delete: Item bị ẩn trên GET List nhưng admin vẫn thấy?
```

---

## 📊 Bảng HTTP Status Code hay gặp

| Status Code | Tên | Ý nghĩa | Khi nào gặp |
|------------|-----|---------|------------|
| `200` | OK | Thành công, có trả dữ liệu | GET, PUT thành công |
| `201` | Created | Tạo mới thành công | POST thành công |
| `204` | No Content | Thành công, không có dữ liệu trả về | DELETE thành công |
| `400` | Bad Request | Request sai (thiếu field, sai kiểu...) | Validation lỗi |
| `401` | Unauthorized | Chưa đăng nhập / Token sai | Thiếu hoặc sai token |
| `403` | Forbidden | Đã đăng nhập nhưng không có quyền | Không đủ permission |
| `404` | Not Found | Không tìm thấy resource | ID không tồn tại |
| `405` | Method Not Allowed | Sai method | Dùng POST thay vì GET |
| `409` | Conflict | Xung đột dữ liệu | Trùng lặp, vi phạm ràng buộc |
| `422` | Unprocessable Entity | Dữ liệu không xử lý được | Logic validation lỗi |
| `429` | Too Many Requests | Gọi quá nhiều | Rate limit exceeded |
| `500` | Internal Server Error | Lỗi phía server | Server crash, bug code |
| `503` | Service Unavailable | Server không khả dụng | Bảo trì, overload |

> **Mẹo nhớ nhanh:**
> ```
> 2xx → Thành công ✅
> 4xx → Lỗi do CLIENT (do bạn gửi sai) ❌
> 5xx → Lỗi do SERVER (do server bị lỗi) 💥
> ```

---

## ✅ Checklist tổng hợp

Dùng bảng này để đảm bảo bạn đã test đủ trước khi báo cáo kết quả:

### 📋 Checklist cho mỗi API

```
THÔNG TIN CƠ BẢN
□ Đã xác định đúng Method (GET/POST/PUT/DELETE)?
□ URL đúng không, có typo không?
□ Headers đầy đủ chưa (Content-Type, Authorization)?
□ Body đúng format JSON chưa (dùng JSONLint.com để validate)?

HAPPY PATH
□ Đã test với dữ liệu hợp lệ đầy đủ?
□ Status code trả về đúng expected?
□ Response body có đúng structure không?
□ Kiểu dữ liệu từng field đúng không?
□ Dữ liệu trả về chính xác, không bị thiếu/thừa?

AUTHENTICATION
□ Test không có token → 401?
□ Test token sai → 401?
□ Test token hết hạn → 401?
□ Test token không đủ quyền → 403?

VALIDATION (POST/PUT)
□ Thiếu từng field bắt buộc?
□ Sai kiểu dữ liệu (số thay chuỗi, chuỗi thay số)?
□ Giá trị ngoài range (âm, quá lớn, quá nhỏ)?
□ String rỗng cho field bắt buộc?
□ Body rỗng {}?

NOT FOUND (GET Detail / PUT / DELETE)
□ ID không tồn tại → 404?
□ ID = 0 → 400 hoặc 404?
□ ID âm → 400 hoặc 404?
□ ID là chữ thay số → 400?

PERFORMANCE
□ Response time < 2000ms (GET List)?
□ Response time < 1000ms (GET Detail)?
□ Response time < 3000ms (POST/PUT/DELETE)?
```

---

## 💡 Tips cho Fresher

### 🛠️ Tools nên dùng
```
Postman    → GUI dễ dùng, tốt để bắt đầu
Insomnia   → Nhẹ hơn Postman, giao diện sạch
Thunder Client → Extension trong VS Code
curl       → Command line, mạnh nhưng khó hơn
```

### 📝 Quy trình test một API mới
```
Bước 1: Đọc kỹ tài liệu (API Document / Swagger)
Bước 2: Test Happy Path trước - dùng dữ liệu đúng hoàn toàn
Bước 3: Ghi lại response mẫu để so sánh sau
Bước 4: Test Unhappy Path - thay đổi từng field một
Bước 5: Test Edge Cases - biên, đặc biệt, boundary
Bước 6: Verify data - kiểm tra database hoặc gọi GET lại
Bước 7: Ghi bug report nếu phát hiện vấn đề
```

### 🐛 Cách viết Bug Report khi test API
```
Title: [API] POST /products - Tạo sản phẩm thành công dù thiếu field "name"

Environment: Staging
Method: POST
URL: https://api-staging.example.com/v1/products

Request Body:
{
  "price": 1000000,
  "category_id": 5
}

Expected: Status 400 - Bad Request với message "name là bắt buộc"
Actual:   Status 201 - Created, sản phẩm được tạo với name = null

Steps to reproduce:
1. Mở Postman
2. POST đến URL trên với body như trên
3. Quan sát response

Severity: High (dữ liệu không hợp lệ được tạo vào database)
```

### ⚡ Những lỗi Fresher hay mắc phải
```
❌ Quên gắn Authorization header → Luôn nhận 401, nghĩ API bị lỗi
   ✅ Fix: Kiểm tra Headers tab trong Postman trước

❌ Gửi Body dạng text thay vì JSON
   ✅ Fix: Trong Postman → Body → chọn "raw" → chọn "JSON" ở dropdown

❌ Chỉ test Happy Path, bỏ qua Unhappy Path
   ✅ Fix: Dùng checklist này, tick từng mục

❌ Không verify data sau khi POST/PUT/DELETE
   ✅ Fix: Sau mỗi thao tác, gọi thêm GET Detail để xác nhận

❌ Copy URL sai (có khoảng trắng ẩn, thiếu https://)
   ✅ Fix: Paste URL vào text editor để kiểm tra trước

❌ Nhầm 401 vs 403: "Chúng đều là lỗi auth mà"
   ✅ Fix: 401 = chưa đăng nhập/token sai, 403 = đã đăng nhập nhưng không có quyền
```

---

## 📚 Tài liệu tham khảo

```
Swagger / OpenAPI  → Xem tài liệu API của project bạn đang test
JSONLint.com       → Validate JSON body trước khi gửi
httpstatuses.com   → Tra cứu ý nghĩa HTTP Status Code
Postman Learning   → postman.com/learning
REST API Tutorial  → restfulapi.net
```

---

> 📌 **Lời kết:**
> Template này là điểm khởi đầu, không phải công thức cứng nhắc.
> Mỗi dự án có đặc thù riêng - hãy luôn đọc tài liệu của team, hỏi Dev khi không chắc,
> và quan trọng nhất là **tư duy "điều gì có thể sai?"** trước mỗi test case.
> Chúc bạn test vui và tìm được nhiều bug! 🐛