Các bài kiểm thử Playwright của bạn đã vượt qua: nút đăng nhập nhấp được, dashboard hiển thị, biểu đồ render đúng vị trí. Nhưng khách hàng vẫn báo lỗi vì số liệu trên biểu đồ sai. Nguyên nhân: API trả về 200 OK với payload lỗi, còn bài kiểm thử end-to-end chỉ kiểm tra UI có xuất hiện hay không. Đây là khoảng trống mà kiểm thử trình duyệt đơn thuần không xử lý tốt. Bạn cần thêm khẳng định API để xác thực contract, schema và logic phản hồi. Các công cụ như Apidog giúp bạn kiểm thử API cùng mức độ nghiêm ngặt như kiểm thử UI, sau đó chạy cả hai trong CI.
TL;DR
Bạn có thể xác thực API trong Playwright bằng cách kết hợp:
- fixture
requestcủa Playwright để gọi API trực tiếp -
page.routeđể mock hoặc intercept network - kịch bản Apidog dựa trên cùng một đặc tả OpenAPI
- fixture dùng chung cho payload, token và dữ liệu kiểm thử
- CI chạy song song Playwright và Apidog CLI
Mục tiêu: một thay đổi contract hoặc payload sai sẽ bị phát hiện ở lớp UI hoặc lớp API trước khi vào production.
Khi nào Playwright chưa đủ?
Một bài kiểm thử Playwright thường kiểm tra:
- người dùng đăng nhập được
- trang load thành công
- phần tử UI xuất hiện
- user flow chạy hết
Nhưng nó không tự đảm bảo API phía sau đang trả đúng dữ liệu.
Ví dụ lỗi dễ bị bỏ qua:
1. Payload đổi shape nhưng UI vẫn render
API đổi trường:
{
"total_count": 120
}
thành:
{
"totalCount": 120
}
Nếu UI fallback về 0 hoặc vẫn render một giá trị nào đó, test UI có thể vẫn pass.
2. Sai logic nghiệp vụ
API coupon đáng lẽ giảm 15%, nhưng backend trả 10%.
UI chỉ hiển thị giá trị API trả về, nên Playwright vẫn thấy “có discount” và pass nếu bạn không kiểm tra sâu.
3. Không bao phủ error path
Playwright thường chạy happy path. Trong khi API có nhiều nhánh:
-
400validation error -
401token hết hạn -
409idempotency conflict -
429rate limit -
500partial failure
Nếu chỉ test UI, các nhánh này thường không được kiểm tra.
Cách chia hợp lý:
- Playwright: kiểm tra luồng UI, tương tác người dùng, network interception, smoke API check gần hành động UI.
- Apidog: kiểm tra schema, contract, workflow API nhiều bước, business rule và error path.
Cả hai nên dùng cùng một đặc tả OpenAPI để tránh có hai “nguồn sự thật”. Nếu bạn muốn bắt đầu theo hướng contract-first, xem thêm bài viết về quy trình API design-first.
Nếu bạn đang so sánh công cụ kiểm thử API, có thể tham khảo thêm các công cụ kiểm thử API dành cho kỹ sư QA.
Kiến trúc đề xuất
Thiết lập tối thiểu:
repo/
├─ openapi.yaml
├─ fixtures/
│ └─ order.json
├─ tests/
│ ├─ fixtures/
│ │ └─ api.ts
│ ├─ orders.spec.ts
│ └─ dashboard.spec.ts
├─ apidog/
│ └─ scenarios/
│ └─ checkout.json
├─ playwright.config.ts
└─ package.json
Nguyên tắc:
-
openapi.yamllà contract chính. -
fixtures/chứa payload dùng chung. - Playwright dùng fixture để gọi API hoặc mock network.
- Apidog import cùng OpenAPI spec và dùng cùng payload.
- CI chạy cả hai bộ test.
Nếu cần cài công cụ trước, bạn có thể tải Apidog.
Tạo fixture API dùng chung trong Playwright
Tạo file tests/fixtures/api.ts:
// tests/fixtures/api.ts
import { test as base, APIRequestContext, expect } from '@playwright/test';
import { readFileSync } from 'fs';
import path from 'path';
type ApiFixtures = {
apiRequest: APIRequestContext;
authToken: string;
sampleOrder: Record<string, unknown>;
};
export const test = base.extend<ApiFixtures>({
apiRequest: async ({ playwright }, use) => {
const ctx = await playwright.request.newContext({
baseURL: process.env.API_BASE_URL ?? 'https://api.staging.example.com',
extraHTTPHeaders: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
await use(ctx);
await ctx.dispose();
},
authToken: async ({ apiRequest }, use) => {
const res = await apiRequest.post('/auth/token', {
data: {
email: 'qa@example.com',
password: process.env.QA_PASSWORD,
},
});
expect(res.status()).toBe(200);
const body = await res.json();
await use(body.access_token);
},
sampleOrder: async ({}, use) => {
const raw = readFileSync(
path.join(__dirname, '..', '..', 'fixtures', 'order.json'),
'utf8',
);
await use(JSON.parse(raw));
},
});
export { expect };
Sau đó trong test, import từ fixture này thay vì @playwright/test.
Ví dụ tests/orders.spec.ts:
// tests/orders.spec.ts
import { test, expect } from './fixtures/api';
test('POST /orders trả về order hợp lệ với coupon SAVE15', async ({
apiRequest,
authToken,
sampleOrder,
}) => {
const res = await apiRequest.post('/orders', {
headers: {
Authorization: `Bearer ${authToken}`,
},
data: {
...sampleOrder,
coupon: 'SAVE15',
},
});
expect(res.status()).toBe(201);
const body = await res.json();
expect(body).toMatchObject({
id: expect.any(String),
status: 'pending',
discount_pct: 15,
total_cents: expect.any(Number),
});
expect(body.total_cents).toBeLessThan(sampleOrder.subtotal_cents);
});
Điểm quan trọng: test này không chỉ kiểm tra 201, mà còn kiểm tra business rule discount_pct: 15.
Import cùng OpenAPI spec vào Apidog
Trong Apidog:
- Mở project.
- Chọn Import.
- Trỏ đến cùng file
openapi.yaml. - Kiểm tra endpoint, request example, response schema được tạo.
- Lưu payload fixture dưới dạng Environment Variables hoặc Data Sets.
- Tạo scenario cho các workflow quan trọng.
Ví dụ scenario API cho checkout:
1. POST /auth/token
2. POST /orders
3. GET /orders/{id}
4. POST /payments
5. GET /payments/{id}
6. POST /orders/{id}/cancel
Ở mỗi bước, thêm assertion:
- status code
- JSON schema
- required fields
- enum values
- business value như
discount_pct,payment_status,refund_status
Playwright bắt lỗi ở mức UI và một số semantic assertion quan trọng. Apidog bắt lỗi schema, contract và workflow API sâu hơn.
Nếu bạn mới với kiểm thử dựa trên đặc tả, xem thêm luồng công việc API design-first.
Đối với nhóm đang dùng Postman, bài viết về các lựa chọn thay thế Postman tự lưu trữ có thể hữu ích khi đánh giá migration.
Mock API trong Playwright bằng page.route
Khi bạn muốn test UI mà không phụ thuộc backend, dùng page.route.
Ví dụ dashboard cần danh sách order:
import { test, expect } from './fixtures/api';
test('dashboard hiển thị danh sách order khi dùng dữ liệu mock', async ({
page,
sampleOrder,
}) => {
await page.route('**/api/orders', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
orders: [sampleOrder],
}),
});
});
await page.goto('/dashboard');
await expect(page.getByTestId('order-row')).toHaveCount(1);
});
Quy tắc thực tế:
- Dùng
page.routeđể cô lập UI. - Không xem mock là thay thế cho kiểm thử API thật.
- Response mock nên lấy từ cùng fixture hoặc cùng OpenAPI example.
- Apidog vẫn cần chạy scenario trên backend thật hoặc mock server riêng.
Thiết lập CI chạy Playwright và Apidog
Ví dụ GitHub Actions:
name: tests
on:
- push
- pull_request
jobs:
playwright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
env:
API_BASE_URL: ${{ secrets.API_BASE_URL }}
QA_PASSWORD: ${{ secrets.QA_PASSWORD }}
apidog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm i -g apidog-cli
- run: apidog-cli run ./apidog/scenarios/checkout.json --reporters cli,junit
env:
API_BASE_URL: ${{ secrets.API_BASE_URL }}
Nếu một job fail, PR không được merge.
Bạn có thể dùng junit reporter để CI hiển thị lỗi assertion trực tiếp trên PR. Tài liệu GitHub Actions có thêm hướng dẫn về cache, matrix build và parallel jobs.
Với nhóm nhỏ hoặc không có QA chuyên trách, xem thêm hướng dẫn chọn công cụ kiểm thử API cho kỹ sư QA.
Phát hiện schema drift sớm
Một lỗi phổ biến là backend thay đổi response nhưng không cập nhật spec.
Ví dụ:
{
"discount_pct": 15
}
bị đổi thành:
{
"discountPercent": 15
}
Để phát hiện sớm:
- Yêu cầu mọi thay đổi API phải cập nhật
openapi.yaml. - Review thay đổi OpenAPI trong PR.
- Chạy Apidog schema validation trong CI.
- Có thể thêm job nightly so sánh OpenAPI live với bản trong repo.
- Fail build nếu phát hiện breaking change không được phê duyệt.
Cấu hình Playwright nên dùng
Trong playwright.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({
retries: 2,
workers: 4,
reporter: [['html'], ['list']],
use: {
baseURL: process.env.WEB_BASE_URL ?? 'https://staging.example.com',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
});
Khuyến nghị:
-
trace: 'on-first-retry'để debug flaky test. -
retries: 2là đủ. Nếu cần nhiều hơn, test hoặc môi trường đang có vấn đề. - Dùng
workersđể song song hóa. - Không hard-code token hoặc password trong test.
Tag test theo mức độ ưu tiên
Bạn không cần chạy mọi thứ trên mỗi commit.
Một cách chia:
@smoke chạy trên mọi push
@regression chạy trên PR vào main
@nightly chạy mỗi đêm
Ví dụ:
import { test, expect } from './fixtures/api';
test('@smoke user có thể mở dashboard', async ({ page }) => {
await page.goto('/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
Chạy smoke test:
npx playwright test --grep @smoke
Với Apidog, bạn có thể chia scenario theo nhóm:
apidog/scenarios/smoke/
apidog/scenarios/regression/
apidog/scenarios/nightly/
Những lỗi nên tránh
- Chỉ assert
status === 200. - Không kiểm tra body.
- Hard-code bearer token.
- Dùng fixture khác nhau giữa Playwright và Apidog.
- Không chạy Apidog CLI trong CI.
- Dùng mock
page.routethay cho API test thật. - Để OpenAPI spec không được review.
- Cho phép schema drift chỉ bằng warning.
Nếu bạn tạo test bằng AI hoặc kiểm thử agent API, xem thêm cách kiểm thử API của tác nhân AI. Với mock và test generation, bài viết về tạo kiểm thử API có hỗ trợ AI cũng đáng tham khảo.
So sánh các lựa chọn công cụ
| Stack | Điểm mạnh | Điểm yếu | Phù hợp với |
|---|---|---|---|
Playwright một mình với fixture request
|
Một công cụ, nhanh, dễ tích hợp với test UI | Schema validation hạn chế, workflow API nhiều bước khó quản lý | Nhóm nhỏ, API đơn giản |
| Playwright + Postman | Hệ sinh thái quen thuộc, có Newman CLI | Collection có thể lệch khỏi OpenAPI, dễ phát sinh hai nguồn sự thật | Nhóm đã dùng Postman lâu năm |
| Playwright + Apidog | Dùng chung OpenAPI, schema validation, mock, CLI cho CI, workflow design-first | Cần học thêm công cụ và giữ kỷ luật với spec | Nhóm muốn kiểm thử API dựa trên contract |
| Cypress + plugin cy-api | Phù hợp với nhóm đang dùng Cypress | API testing không phải trọng tâm chính, plugin ít hoàn thiện hơn | Codebase Cypress hiện có |
| Pact | Consumer-driven contract mạnh cho microservices | Cần broker, setup phức tạp, không tập trung vào UI | Tổ chức microservice có nhiều consumer nội bộ |
Nếu bạn chuyển từ công cụ cũ, tham khảo thêm:
- các lựa chọn thay thế script Groovy của SoapUI
- các lựa chọn thay thế ReadyAPI
- các tiện ích mở rộng VSCode dành cho REST client
Use case thực tế
Thanh toán thương mại điện tử
Playwright kiểm tra luồng:
cart → checkout → payment → confirmation
Apidog kiểm tra chuỗi API:
create payment intent → fraud check → inventory deduction → refund
Nếu payment gateway đổi field error_code thành errorCode, Apidog có thể bắt lỗi schema rất nhanh. Playwright có thể chỉ thấy màn hình “thanh toán thất bại” chung chung.
Dashboard SaaS với biểu đồ
Playwright snapshot xác nhận chart render.
Apidog kiểm tra endpoint aggregation trả đúng:
- tổng
- phần trăm
- series theo thời gian
- dữ liệu p95/p99
- enum metric type
Nếu API âm thầm bỏ qua outlier, chart vẫn có thể trông “ổn”, nhưng API assertion sẽ fail.
Workflow webhook
Playwright kiểm tra portal người dùng.
Apidog kiểm tra:
- webhook signature
- retry logic
- duplicate webhook ID
- idempotency
- eventual consistency window
Đây là các logic khó kiểm thử chỉ bằng browser automation.
Kết luận
Playwright rất mạnh cho kiểm thử UI, nhưng không nên là lớp bảo vệ duy nhất cho API. Một thiết lập Playwright + Apidog hiệu quả nên có:
- một
openapi.yamllàm contract chung - fixture dùng chung cho payload và test data
- Playwright kiểm tra UI flow và một lớp API smoke test
- Apidog kiểm tra schema, contract, workflow và error path
- CI chạy cả hai bộ test
- mock server hoặc
page.routeđể phát triển khi backend chưa ổn định
Bắt đầu nhỏ: chọn một flow quan trọng như đăng ký hoặc thanh toán. Tạo fixture Playwright, import OpenAPI vào Apidog, viết scenario tương ứng, rồi chạy cả hai trong CI.
Câu hỏi thường gặp
Tôi có thể xác thực API trong Playwright mà không cần Apidog không?
Có. Bạn có thể dùng fixture request và expect thủ công trong Playwright. Cách này phù hợp cho smoke test hoặc một vài endpoint quan trọng. Nhưng nếu cần schema validation, workflow nhiều bước, mock và error path ở quy mô lớn, một công cụ chuyên dụng như Apidog sẽ phù hợp hơn.
Xem thêm so sánh công cụ kiểm thử API dành cho kỹ sư QA.
Tôi có bắt buộc phải có OpenAPI spec không?
Không bắt buộc, nhưng rất nên có. Nếu không có OpenAPI, bạn vẫn có thể chạy Playwright và Apidog song song, nhưng sẽ mất nguồn contract chung và dễ phải duy trì fixture ở nhiều nơi.
Xử lý authentication giữa Playwright và Apidog như thế nào?
Trong Playwright, dùng fixture hoặc beforeAll để lấy token mới từ endpoint auth.
Trong Apidog, lưu token vào environment variable sau bước login.
Không hard-code token trong source code hoặc fixture.
Apidog có thay thế hoàn toàn Playwright không?
Không. Apidog kiểm thử API workflow. Playwright kiểm thử browser workflow. Bạn vẫn cần Playwright cho:
- click flow
- layout
- visible text
- form interaction
- rendering
- accessibility check
- browser-specific behavior
Hai công cụ này bổ sung cho nhau.
Nếu backend staging không ổn định thì sao?
Dùng mock server của Apidog hoặc page.route trong Playwright để cô lập UI. Tuy nhiên, vẫn nên có job chạy định kỳ với backend thật để phát hiện lỗi tích hợp.
Làm sao giữ CI nhanh khi test suite lớn dần?
- Chạy
@smoketrên mỗi push. - Chạy regression trên PR vào
main. - Chạy toàn bộ Apidog scenario vào ban đêm.
- Song song hóa Playwright bằng
workers. - Song song hóa scenario API nếu CLI hỗ trợ.
- Cache dependency trong GitHub Actions.
Tôi có cần gói Apidog trả phí cho CI không?
Hãy kiểm tra trang giá hiện tại trước khi triển khai ở quy mô lớn. Với nhóm nhỏ, gói miễn phí thường đủ để bắt đầu; với CI và cộng tác nhiều người, bạn nên xác nhận giới hạn hiện tại của Apidog.
Top comments (0)