NỘI DUNG BÀI HỌC
🌟 Phần 1: Tư Duy Kiến Trúc (Architectural Mindset)
🏗️ Phần 2: Thiết Kế Chi Tiết (Deep Dive)
🚀 Phần 3: Workflow Hoạt Động
🛠️ Phần 4: Khả Năng Mở Rộng (Scalability)
🛑 Vấn đề: Tại sao chúng ta cần Architecture?
Trước khi đi vào giải pháp, hãy xem xét cách chúng ta thường bắt đầu viết test API (Cách làm ngây thơ):
// ❌ Bad Practice: Spaghetti Code
test('Get products', async ({ request }) => {
// 1. Login lặp đi lặp lại
const loginRes = await request.post('/auth/login', {
data: { username: 'admin', password: '123' }
});
const token = (await loginRes.json()).access_token;
// 2. Hardcode URL và không có Type-check
const res = await request.get('/api/products', {
headers: { Authorization: `Bearer ${token}` }
});
// 3. Khó debug dữ liệu trả về
expect((await res.json()).data).toHaveLength(5);
});
Nỗi đau (Pain points):
-
Code lặp (DRY Violation): Login ở mọi test case. Nếu API Login thay đổi, bạn phải sửa hàng trăm file.
-
Hardcode: URL /api/products rải rác khắp nơi.
-
No Type Safety: res.json() trả về any. IDE không gợi ý code, dễ gõ sai tên trường (ví dụ: product.name vs product.fullName).
-
Khó bảo trì: Test trộn lẫn logic gọi API và logic kiểm thử.
Tư Duy Kiến Trúc (Architectural Mindset)
Trụ cột của Framework hiện đại
Orchestration (Điều phối): Quản lý thứ tự chạy test (Setup chạy trước, Test chạy sau)
Abstraction (Trừu tượng hóa): Che giấu sự phức tạp của HTTP Request
Injection (Tiêm phụ thuộc): Cung cấp tài nguyên (Service, Token) vào test
Validation (Kiểm soát): Đảm bảo tính đúng đắn của dữ liệu
🏗️ Thiết Kế Chi Tiết (Deep Dive)
Trụ cột 1: Điều phối với Project Dependencies
Thay vì dùng beforeAll hay globalSetup (khó debug), ta coi việc "Đăng nhập" là một Test Project riêng biệt.
projects: [
{ name: 'setup', testMatch: /auth\.setup\.ts/ }, // Chạy trước
{
name: 'api',
dependencies: ['setup'], // Đợi setup xong mới chạy
testMatch: /.*\.spec\.ts/
}
]
Chiến thuật "Smart Login" trong Setup:
Không login mù quáng mỗi lần chạy. Setup hoạt động như một Gatekeeper:
-
Check file Token có tồn tại?
-
Decode JWT để check hạn sử dụng (Expiry).
-
Chỉ Login lại khi thực sự cần thiết.
Tăng tốc độ chạy test lên đáng kể khi chạy CI/CD lặp lại.
Trụ cột 2: Abstraction với Service Object Pattern
Chúng ta tạo ra các "Class" đại diện cho các nghiệp vụ API, thay vì gọi request.get() trần trụi.
-
BaseService: Lớp cha mẫu mực.
-
Tự động inject Authorization header nếu có token.
-
Xử lý URL prefix (baseURL).
-
Wrap các method GET/POST/PUT/DELETE với Generic Types <T>.
-
-
Domain Services (AuthService, ProductService...): Kế thừa BaseService.
-
Chứa các hàm nghiệp vụ: getProducts(), createOrder().
-
Giấu kín các đường dẫn endpoint (/api/v1/products).
-
Lợi ích: Khi Dev đổi endpoint API, QA chỉ cần sửa đúng 1 dòng trong Service Class.
Trụ cột 3: Dependency Injection với Fixtures
Đây là sức mạnh thực sự của Playwright. Chúng ta xây dựng một dây chuyền lắp ráp (Assembly Line):
-
Base Test: Cung cấp request (có sẵn của Playwright).
-
Auth Fixture:
-
Kế thừa Base.
-
Nhiệm vụ: Đọc file Token do project Setup sinh ra.
-
Cung cấp: authToken.
-
-
Service Fixture:
-
Kế thừa Auth Fixture.
-
Nhiệm vụ: Khởi tạo new ProductService(request, authToken).
-
Cung cấp: productService, userService, orderService.
-
Kết quả trong Test Case:
test('Ví dụ', async ({ productService }) => {
// productService đã SẴN SÀNG dùng.
// Đã có request context.
// Đã có token header.
await productService.getAll();
});
Trụ cột 4: Type Safety với Interface
Không bao giờ dùng any. Mọi Response trả về từ API phải được map với một Interface.
-
Giúp Code gợi ý (Intellisense): Gõ response. là hiện ra danh sách trường.
-
Phát hiện lỗi sớm: Nếu API đổi response (vd: đổi id thành _id), TypeScript sẽ báo đỏ ngay trong lúc code, không cần chờ chạy test mới biết lỗi.
🚀 Workflow Hoạt Động
Hãy hình dung luồng chạy khi bạn gõ lệnh npx playwright test:
-
Khởi động: Playwright đọc config, thấy có dependency.
-
Giai đoạn 1 (Setup Project):
-
Chạy auth.setup.ts.
-
Kiểm tra token
→Login (nếu cần)→Ghi file JSON. -
Report: ✅ Setup passed.
-
-
Giai đoạn 2 (Test Project):
-
Playwright khởi tạo các Worker.
-
Với mỗi file test:
-
Fixture kích hoạt
→Đọc file JSON→Tạo Service Instance. -
Inject Service vào hàm test().
-
-
Test chạy logic nghiệp vụ.
-
Report: ✅ Test passed.
-
🛠️ Khả Năng Mở Rộng (Scalability)
Framework tốt là Framework dễ dàng thêm mới tính năng. Để thêm test cho module "User Management", ta làm theo quy trình chuẩn ISTF:
-
I - Interface: Định nghĩa User và CreateUserDto trong interfaces/user.interface.ts.
-
S - Service: Tạo UserService.ts kế thừa BaseService, viết hàm getMe(), updateProfile().
-
T - Test Fixture: Đăng ký userService vào services.fixture.ts.
-
F - Feature Test: Tạo file user.spec.ts và gọi ({ userService }) để test.
📝 Tổng Kết
Hôm nay chúng ta không chỉ học cách viết code, mà học cách kiến trúc phần mềm.
-
Setup tách biệt giúp debug dễ dàng.
-
Services giúp code dễ bảo trì.
-
Fixtures giúp code test gọn gàng, tập trung vào nghiệp vụ.
-
TypeScript giúp code an toàn, tránh lỗi ngớ ngẩn.
Đây là nền tảng để các bạn xây dựng các hệ thống Automation Test quy mô lớn hàng nghìn test cases.
