Bỏ qua

Module 06: Code Review — Checklist và Best Practices

🎯 Mục Tiêu Module

  • Hiểu tại sao Code Review quan trọng
  • Nắm được Checklist đánh giá code Playwright
  • Thực hành review code AI sinh ra
  • Biết cách tối ưu code

6.1. Tại Sao Code Review Quan Trọng?

Vấn đề: Code AI sinh ra chỉ là nháp

┌─────────────────────────────────────────────────────┐
│                                                     │
│  Code AI sinh ra:                                   │
│  ✅ Chạy được                                       │
│  ❌ Chưa tối ưu                                     │
│  ❌ Có thể có anti-pattern                          │
│  ❌ Có thể thiếu assertion                          │
│  ❌ Có thể dùng locator không ổn định               │
│                                                     │
│  → Cần QC review trước khi sử dụng chính thức      │
└─────────────────────────────────────────────────────┘

Vai trò của QC trong Code Review

QC Manual trong Code Review:

1. Đọc code và hiểu logic
2. Kiểm tra locator có ổn định không
3. Kiểm tra có assertion đầy đủ không
4. Kiểm tra có anti-pattern không
5. Duyệt hoặc yêu cầu chỉnh sửa

6.2. Code Review Checklist

Checklist đầy đủ

# ═══════════════════════════════════════════════════
# CODE REVIEW CHECKLIST — Playwright Test Script
# ═══════════════════════════════════════════════════

## 1. Locator Quality
□ Không dùng waitForTimeout
□ Không dùng locator index (.nth())
□ Locator tuân thủ hierarchy (data-testid > id > aria-label > text > CSS)
□ Không dùng XPath trừ khi bất khả kháng
□ Locator ngắn gọn, dễ đọc

## 2. Assertion
□ Có assertion sau mỗi step quan trọng
□ Assertion cụ thể (không chỉ check visible)
□ Có assertion cho URL khi cần
□ Có assertion cho text khi cần

## 3. Code Structure
□ Test name mô tả được hành động
□ Mỗi test case độc lập (không phụ thuộc test khác)
□ Có comment giải thích logic phức tạp
□ Code dễ đọc, dễ hiểu

## 4. Test Data
□ Không hardcode test data trong script
□ Test data tách riêng (nếu cần)
□ Dùng biến cho giá trị thường dùng

## 5. Error Handling
□ Có screenshot khi fail (nếu cần)
□ Có error message rõ ràng
□ Không swallow error

## 6. Performance
□ Không dùng waitForTimeout
□ Dùng waitForSelector / waitForLoadState
□ Không chờ quá lâu (timeout hợp lý)

## 7. Best Practices
□ Tuân thủ Page Object Model (nếu project lớn)
□ Dùng fixtures (nếu cần)
□ Dùng beforeEach cho setup chung

6.3. Các Anti-Pattern Cần Tránh

Anti-Pattern 1: waitForTimeout

// ❌ Anti-pattern: Chờ cố định
await page.waitForTimeout(5000);

// ✅ Nên dùng: Chờ element xuất hiện
await page.locator('#result').waitFor({ state: 'visible' });

// ✅ Hoặc: Chờ trang load
await page.waitForLoadState('networkidle');

Anti-Pattern 2: Locator index

// ❌ Anti-pattern: Dùng index
await page.locator('button').nth(2).click();

// ✅ Nên dùng: Locator cụ thể
await page.locator('button:has-text("Submit")').click();

Anti-Pattern 3: Không có assertion

// ❌ Anti-pattern: Không kiểm tra kết quả
test('test login', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.locator('#username').fill('testuser');
  await page.locator('#password').fill('Test@123');
  await page.locator('#login-btn').click();
  // Không có assertion → Không biết pass hay fail
});

// ✅ Nên dùng: Có assertion rõ ràng
test('test login', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.locator('#username').fill('testuser');
  await page.locator('#password').fill('Test@123');
  await page.locator('#login-btn').click();

  // Assertion: Kiểm tra đăng nhập thành công
  await expect(page.locator('.welcome-message')).toBeVisible();
  await expect(page).toHaveURL(/dashboard/);
});

Anti-Pattern 4: Hardcode test data

// ❌ Anti-pattern: Hardcode trong code
await page.locator('#username').fill('testuser123');
await page.locator('#password').fill('Test@123456');

// ✅ Nên dùng: Tách riêng test data
const TEST_DATA = {
  username: 'testuser123',
  password: 'Test@123456',
};

await page.locator('#username').fill(TEST_DATA.username);
await page.locator('#password').fill(TEST_DATA.password);

Anti-Pattern 5: Test phụ thuộc nhau

// ❌ Anti-pattern: Test phụ thuộc
test('test 1: login', async ({ page }) => {
  // Login
});

test('test 2: add to cart', async ({ page }) => {
  // Phụ thuộc test 1 đã login → Có thể fail nếu chạy riêng
});

// ✅ Nên dùng: Mỗi test độc lập
test('test add to cart', async ({ page }) => {
  // Login trước khi test
  await page.goto('/login');
  await page.locator('#username').fill('testuser');
  await page.locator('#password').fill('Test@123');
  await page.locator('#login-btn').click();

  // Test thêm giỏ hàng
  await page.goto('/shop');
  // ...
});

6.4. Ví Dụ Review Code

Code AI sinh ra (trước khi review)

import { test, expect } from '@playwright/test';

test('test', async ({ page }) => {
  await page.goto('https://practice.automationtesting.in/my-account/');
  await page.locator('#username').click();
  await page.locator('#username').fill('testuser123');
  await page.locator('#password').click();
  await page.locator('#password').fill('Test@123456');
  await page.locator('[name="login"]').click();
  await page.waitForTimeout(3000);
  await page.locator('text=Orders').click();
});

Review comments

Dòng 3: Tên test quá ngắn "test" → Nên đổi thành "test login success"
Dòng 5: Không cần click trước khi fill → Có thể bỏ
Dòng 7: Không cần click trước khi fill → Có thể bỏ
Dòng 9: Dùng waitForTimeout → Nên dùng waitForLoadState hoặc waitForSelector
Dòng 10: Không có assertion → Không biết test pass hay fail

Code sau khi review

import { test, expect } from '@playwright/test';

test('test login success', async ({ page }) => {
  await page.goto('https://practice.automationtesting.in/my-account/');

  await page.locator('#username').fill('testuser123');
  await page.locator('#password').fill('Test@123456');

  await page.locator('[name="login"]').click();

  // Chờ trang load xong
  await page.waitForLoadState('networkidle');

  // Assertion: Kiểm tra đăng nhập thành công
  await expect(page.locator('.woocommerce-MyAccount-content')).toBeVisible();

  // Kiểm tra có link Orders
  await expect(page.locator('text=Orders')).toBeVisible();
});

6.5. Quy Trình Code Review

┌─────────────────────────────────────────────────────┐
│  Quy trình Code Review:                             │
│                                                     │
│  1. AI sinh code nháp                               │
│         ↓                                           │
│  2. Skill 2 (Code Reviewer) check tự động           │
│         ↓                                           │
│  3. QC Manual review chi tiết                        │
│         ↓                                           │
│  4. QC duyệt hoặc yêu cầu chỉnh sửa                │
│         ↓                                           │
│  5. Code chính thức (Approved)                      │
└─────────────────────────────────────────────────────┘

📝 Bài Tập

Bài Tập 1: Review Code AI

Review code sau và tìm tất cả anti-pattern:

import { test, expect } from '@playwright/test';

test('test add to cart', async ({ page }) => {
  await page.goto('https://practice.automationtesting.in/shop/');
  await page.locator('.product').nth(0).click();
  await page.locator('.add_to_cart_button').click();
  await page.waitForTimeout(5000);
  await page.locator('text=View Basket').click();
});

Bài Tập 2: Viết Code Tốt Hơn

Viết lại code bài tập 1 thành code tốt hơn.

Bài Tập 3: Review Code Bạn

Trao đổi code với bạn học và review cho nhau.


✅ Checklist Hoàn Thành Module

  • [ ] Hiểu tại sao Code Review quan trọng
  • [ ] Nắm được Checklist đánh giá
  • [ ] Biết các anti-pattern cần tránh
  • [ ] Thực hành review code
  • [ ] Hoàn thành bài tập