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:

  1. Check file Token có tồn tại?

  2. Decode JWT để check hạn sử dụng (Expiry).

  3. 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):

  1. Base Test: Cung cấp request (có sẵn của Playwright).

  2. Auth Fixture:

    • Kế thừa Base.

    • Nhiệm vụ: Đọc file Token do project Setup sinh ra.

    • Cung cấp: authToken.

  3. 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:

  1. Khởi động: Playwright đọc config, thấy có dependency.

  2. 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.

  3. 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 JSONTạ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:

  1. I - Interface: Định nghĩa UserCreateUserDto trong interfaces/user.interface.ts.

  2. S - Service: Tạo UserService.ts kế thừa BaseService, viết hàm getMe(), updateProfile().

  3. T - Test Fixture: Đăng ký userService vào services.fixture.ts.

  4. 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.

Teacher

Teacher

Nguyên Hoàng

Automation Engineer

With 7+ years of hands-on experience across multiple languages and frameworks. I'm here to share knowledge, helping you turn complex processes into simple and effective solutions.

Cộng đồng Automation Testing Việt Nam:

🌱 Telegram Automation Testing:   Cộng đồng Automation Testing
🌱 
Facebook Group Automation: Cộng đồng Automation Testing Việt Nam
🌱 
Facebook Fanpage: Cộng đồng Automation Testing Việt Nam - Selenium
🌱 Telegram
Manual Testing:   Cộng đồng Manual Testing
🌱 
Facebook Group Manual: Cộng đồng Manual Testing Việt Nam

Chia sẻ khóa học lên trang

Bạn có thể đăng khóa học của chính bạn lên trang Anh Tester để kiếm tiền

Danh sách bài học