Mục lục
- Mục Tiêu Bài Học
- dependencies Là Gì?
- Cú Pháp Cơ Bản
- Dependency Graph Và Topological Sort
- Multi-Dependency — Phụ Thuộc Nhiều Project
- Behavior Khi Dependency Fail
- Pattern Multi-Tier Setup
- CLI Flag Interaction
- Report Và CLI Log
- Use Case Multi-Env
- Circular Dependency
- Phân Biệt dependencies Và teardown
- Pitfall Thường Gặp
- Quiz
- Bài Tiếp Theo
Mục Tiêu Bài Học
Sau khi hoàn thành bài này, bạn sẽ:
- Khai báo
dependenciesđể ràng buộc thứ tự chạy giữa các project. - Hiểu cách Playwright giải quyết dependency graph bằng topological sort.
- Biết behavior khi dependency fail: cascade skip với status
expected-skipped. - Dùng
--no-depsđể bỏ qua dependency khi debug. - Thiết lập pattern multi-tier (DB seed → auth → test).
- Tránh 5 pitfall phổ biến liên quan đến dependency config.
dependencies Là Gì?
Field dependencies là một array chứa tên các project phải hoàn thành trước khi project hiện tại được phép chạy. Đây là cơ chế để mô hình hóa quan hệ phụ thuộc trước-sau giữa các project trong playwright.config.ts.
Vấn đề mà dependencies giải quyết: các test thường yêu cầu trạng thái nền (database được seed, user đã đăng nhập, file artifact đã tồn tại) trước khi chạy. Nếu không có cơ chế ordering, Playwright có thể bắt đầu project test trước khi project setup kịp tạo ra auth.json — dẫn đến race condition.
Trước khi có dependencies (trước v1.31), cách duy nhất để đảm bảo ordering là chạy riêng lẻ qua CLI (npx playwright test --project=setup && npx playwright test --project=main). Từ v1.31, dependencies tích hợp ordering vào trong config, cho phép chạy một lệnh duy nhất mà vẫn đảm bảo thứ tự.
Cú Pháp Cơ Bản
Field dependencies nhận array of string — mỗi string là name của project khác trong cùng config:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
// Project setup: chạy trước, không có dependency
{
name: 'auth-setup',
testMatch: /auth\.setup\.ts/,
},
// Project main: phụ thuộc auth-setup
{
name: 'main',
dependencies: ['auth-setup'],
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json',
},
},
],
});
Khi chạy npx playwright test:
- Playwright đọc toàn bộ
projectsvà xây dựng dependency graph. - Project
auth-setupkhông códependencies→ chạy ngay. - Project
maincódependencies: ['auth-setup']→ đợiauth-setupkết thúc (PASS) rồi mới chạy.
Khi khai báo dependencies trên một project mà không có project nào match tên đó, Playwright throw error khi load config — không phải khi chạy test.
Dependency Graph Và Topological Sort
Playwright xây dựng một directed acyclic graph (DAG) từ tập hợp dependencies, sau đó thực hiện topological sort để xác định thứ tự chạy. Quy tắc:
- Nếu A
dependB, B phải chạy xong trước A. - Nếu A
depend[B, C], cả B và C phải xong trước A. B và C có thể chạy parallel với nhau nếu chúng không phụ thuộc lẫn nhau. - Project không có
dependencieschạy ngay lập tức (ở tầng đầu tiên của graph).
Ví dụ dependency chain 3 tầng:
// A → B → C nghĩa là: C chạy trước B, B chạy trước A
projects: [
{
name: 'db-migrate',
testMatch: /migrate\.setup\.ts/,
// Không có dependencies → chạy đầu tiên
},
{
name: 'auth-setup',
testMatch: /auth\.setup\.ts/,
dependencies: ['db-migrate'], // Chờ db-migrate
},
{
name: 'main',
dependencies: ['auth-setup'], // Chờ auth-setup
use: { storageState: 'playwright/.auth/user.json' },
},
]
Thứ tự thực thi:
Tầng 1: db-migrate (không có dependency)
Tầng 2: auth-setup (depend db-migrate)
Tầng 3: main (depend auth-setup)
Khi B và C không phụ thuộc nhau nhưng cùng là dependency của A, chúng chạy parallel:
Tầng 1: setup-db, setup-cache (chạy parallel)
Tầng 2: auth-setup (depend setup-db)
Tầng 3: main (depend auth-setup, setup-cache)
Multi-Dependency — Phụ Thuộc Nhiều Project
Một project có thể liệt kê nhiều dependency trong cùng một array. Tất cả đều phải PASS trước khi project hiện tại chạy:
projects: [
{
name: 'setup-db',
testMatch: /db\.setup\.ts/,
},
{
name: 'setup-auth',
testMatch: /auth\.setup\.ts/,
dependencies: ['setup-db'],
},
{
name: 'setup-cache',
testMatch: /cache\.setup\.ts/,
// Không phụ thuộc setup-db → chạy parallel với setup-db
},
{
name: 'main',
dependencies: ['setup-auth', 'setup-cache'],
// setup-auth và setup-cache đều phải xong trước khi main chạy
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json',
},
},
]
Graph từ config trên:
setup-db ──→ setup-auth ──┐
├──→ main
setup-cache ──────────────┘
Execution order:
Tầng 1: setup-db + setup-cache (parallel)
Tầng 2: setup-auth (depend setup-db)
Tầng 3: main (depend setup-auth + setup-cache)
Lưu ý: setup-cache chạy parallel với setup-db ở tầng 1, không phải sau setup-db. Playwright tối ưu parallelism ở mức tối đa có thể trong giới hạn graph.
Behavior Khi Dependency Fail
Khi một dependency project fail, tất cả project phụ thuộc vào nó sẽ không chạy và được đánh dấu với status expected-skipped (không phải failed). Đây là cascade skip:
// Scenario: db-migrate fail
db-migrate → FAILED
auth-setup → expected-skipped (phụ thuộc db-migrate)
main → expected-skipped (phụ thuộc auth-setup)
Cascade xảy ra theo toàn bộ chiều sâu của graph. Nếu có 5 tầng và tầng 1 fail, cả 4 tầng còn lại đều bị skip.
Status expected-skipped quan trọng vì:
- Test runner không tính là fail — exit code của CI vẫn non-zero do project dependency thực sự fail.
- Trong HTML report, các project bị skip do dependency hiển thị rõ lý do (dependency failed), khác với test được skip chủ động bằng
test.skip(). - Khi fix project dependency và chạy lại, các project downstream sẽ tự động chạy — không cần xóa skip manual.
Ví dụ xem trong CLI output:
1 failed
[db-migrate] › migrate.setup.ts:5:3 › DB migration › run migrations ─────
2 skipped
[auth-setup] (dependency failed)
[main] (dependency failed)
Pattern Multi-Tier Setup
Pattern phổ biến trong dự án thực tế: chia setup thành nhiều phase, mỗi phase là một project riêng. Dependency chain đảm bảo phase sau không bắt đầu trước phase trước hoàn thành:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
// Phase 1: Infrastructure
// Chạy đầu tiên, không có dependency
{
name: 'reset-db',
testMatch: /reset\.setup\.ts/,
},
// Phase 2: Auth setup
// Cả hai project này depend reset-db
// → chúng chạy parallel sau khi reset-db xong
{
name: 'auth-admin',
testMatch: /admin\.setup\.ts/,
dependencies: ['reset-db'],
},
{
name: 'auth-user',
testMatch: /user\.setup\.ts/,
dependencies: ['reset-db'],
},
// Phase 3: Test projects
// Mỗi test project chỉ depend đúng auth project của mình
{
name: 'admin-tests',
dependencies: ['auth-admin'],
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/admin.json',
},
},
{
name: 'user-tests',
dependencies: ['auth-user'],
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json',
},
},
],
});
Graph execution:
Phase 1: reset-db
├──→ auth-admin ──→ admin-tests
Phase 2: └──→ auth-user ──→ user-tests
Phase 3:
Timeline thực tế trên CI (giả sử mỗi phase mất ~30 giây):
t=0s reset-db starts
t=30s reset-db PASS → auth-admin + auth-user start (parallel)
t=60s auth-admin PASS → admin-tests start
auth-user PASS → user-tests start
t=60s+ admin-tests + user-tests chạy parallel
So với chạy tuần tự không dùng dependencies: nếu auth-admin và auth-user chạy serial thì tốn thêm 30 giây. Với dependencies, cả hai chạy parallel ngay sau khi reset-db xong.
CLI Flag Interaction
Có 3 pattern CLI liên quan đến dependencies:
Chạy project có dependency — setup tự động chạy trước
Khi chỉ định --project=main, Playwright tự động phát hiện main có dependency và chạy dependency trước:
# Playwright tự chạy auth-setup trước, sau đó chạy main
npx playwright test --project=main
Điều này có nghĩa là không cần nhớ chạy setup riêng — --project=main đủ để trigger toàn bộ chain.
--no-deps — bỏ qua dependency
Flag --no-deps bỏ qua tất cả dependencies khi chạy project được chỉ định. Hữu ích khi debug test và artifact từ setup lần trước vẫn còn hợp lệ:
# Skip auth-setup, dùng auth.json đã có sẵn từ lần chạy trước
npx playwright test --project=main --no-deps
Dùng --no-deps khi:
- Debug một test cụ thể nhiều lần liên tiếp — không muốn chạy lại setup mỗi lần.
storageStatecòn hợp lệ (session chưa expire).- Setup project chậm và bạn chắc chắn state đã đúng.
Không dùng --no-deps khi session đã expire hoặc DB state cần reset — test sẽ fail với lỗi auth thay vì lỗi rõ ràng.
Chỉ chạy setup
Để refresh artifact mà không chạy test chính:
# Chỉ chạy auth-setup, tạo mới auth.json
npx playwright test --project=auth-setup
# Refresh tất cả setup phase
npx playwright test --project=reset-db --project=auth-admin --project=auth-user
Report Và CLI Log
Setup project (dependency) xuất hiện trong report như bất kỳ project nào khác. Chúng hiển thị riêng với tên project trong ngoặc vuông:
Running 3 projects (auth-setup, admin-tests, user-tests)
[auth-setup] › auth.setup.ts:8:3 › authentication › login as admin ✓ 1.2s
[auth-setup] › auth.setup.ts:20:3 › authentication › login as user ✓ 0.9s
[admin-tests] › dashboard.spec.ts:5:3 › Dashboard › loads correctly ✓ 0.8s
[admin-tests] › dashboard.spec.ts:15:3 › Dashboard › shows analytics ✓ 1.1s
[user-tests] › profile.spec.ts:5:3 › Profile › can update name ✓ 0.7s
Trong HTML report (npx playwright show-report), dependency project hiển thị trong danh sách project riêng. Khi dependency fail, các project downstream hiển thị với label "Skipped (dependency failed)" kèm link đến project dependency failed.
Dependency chain cũng visible trong CLI log khi chạy với --reporter=list: thứ tự các dòng output phản ánh đúng thứ tự thực thi theo graph.
Use Case Multi-Env
Khi cần chạy test trên nhiều environment (staging, prod) mà mỗi env cần setup riêng, khai báo hai setup project riêng biệt và mỗi test project depend đúng setup tương ứng:
export default defineConfig({
projects: [
// Setup cho staging
{
name: 'auth-staging',
testMatch: /auth-staging\.setup\.ts/,
},
// Setup cho prod
{
name: 'auth-prod',
testMatch: /auth-prod\.setup\.ts/,
},
// Test project staging
{
name: 'tests-staging',
dependencies: ['auth-staging'],
use: {
...devices['Desktop Chrome'],
baseURL: 'https://staging.app.com',
storageState: 'playwright/.auth/staging-user.json',
},
},
// Test project prod
{
name: 'tests-prod',
dependencies: ['auth-prod'],
use: {
...devices['Desktop Chrome'],
baseURL: 'https://app.com',
storageState: 'playwright/.auth/prod-user.json',
},
},
],
});
Chạy chỉ staging: npx playwright test --project=tests-staging — tự động trigger auth-staging nhưng không trigger auth-prod.
Về conditional dependency: dependencies là static — được khai báo cứng trong config, không thể thay đổi runtime. Nếu muốn conditional (chọn setup khác nhau dựa trên env var), tạo hai test project riêng với deps khác nhau rồi filter qua --project trên CLI.
Circular Dependency
Circular dependency xảy ra khi A depend B và B depend A (trực tiếp hoặc gián tiếp). Playwright phát hiện cycle khi load config và throw error ngay lập tức — không phải khi bắt đầu chạy test:
// Config này sẽ throw khi load
projects: [
{
name: 'project-a',
dependencies: ['project-b'],
},
{
name: 'project-b',
dependencies: ['project-a'], // Circular: a → b → a
},
]
Error: Playwright: Circular dependency detected in projects: project-a -> project-b -> project-a
Cycle gián tiếp (A → B → C → A) cũng được phát hiện.
Nếu gặp error này, vẽ lại dependency graph trên giấy để tìm điểm đứt cycle. Thường xảy ra khi hai setup project cùng setup chung một resource và nhầm lẫn đặt dependency ngược chiều.
Phân Biệt dependencies Và teardown
Hai field liên quan nhưng hướng ngược nhau:
| Field | Ý nghĩa | Thứ tự |
|---|---|---|
dependencies |
Project hiện tại chờ các project này xong TRƯỚC mới chạy | Dep → Current |
teardown |
Project được chỉ định chạy SAU khi project hiện tại hoàn thành (kể cả khi fail) | Current → Teardown |
projects: [
{
name: 'setup',
testMatch: /setup\.ts/,
},
{
name: 'cleanup',
testMatch: /cleanup\.ts/,
},
{
name: 'main',
dependencies: ['setup'], // setup chạy TRƯỚC main
teardown: 'cleanup', // cleanup chạy SAU main
use: { storageState: 'auth.json' },
},
]
Thứ tự: setup → main → cleanup.
teardown được thiết kế để dọn dẹp sau khi test xong — xóa file artifact, revoke token, reset DB. Bài tiếp theo (bài 58) đi sâu vào teardown.
Pitfall Thường Gặp
Pitfall 1 — Dependency name typo
Tên trong dependencies phải khớp chính xác (case-sensitive) với name của project được khai báo.
// Sai: typo "auth-Setup" (uppercase S)
{
name: 'main',
dependencies: ['auth-Setup'], // Không match 'auth-setup'
}
// Playwright throw khi load config:
// Error: Playwright: Project 'main' depends on unknown project 'auth-Setup'
Error xuất hiện ngay khi load config, trước khi bất kỳ test nào chạy. Dễ phát hiện — không gây silent bug.
Pitfall 2 — Circular dependency
Đã đề cập ở mục 11. Error khi load config với message rõ ràng. Tuy nhiên, với graph lớn (5-6 project), cycle gián tiếp khó nhận ra hơn. Giải pháp: vẽ graph trước khi code.
Pitfall 3 — Quên khai báo dependency, race condition
Đây là pitfall nguy hiểm nhất vì không có error — test chỉ fail ngẫu nhiên.
// Thiếu dependencies: main chạy trước auth-setup xong
// → storageState file chưa tồn tại → test fail với "ENOENT"
{
name: 'main',
// dependencies: ['auth-setup'] ← bị bỏ qua
use: { storageState: 'playwright/.auth/user.json' },
}
Race condition này đặc biệt khó debug vì đôi khi pass (khi auth-setup chạy đủ nhanh), đôi khi fail (khi CI load cao). Luôn khai báo dependencies khi project dùng artifact từ project khác.
Pitfall 4 — testMatch của setup project không đúng
Setup project không có testMatch cụ thể sẽ match mọi file test trong testDir, bao gồm cả file test thông thường. Kết quả: setup project chạy toàn bộ test suite — không phải chỉ file setup.
// Sai: không có testMatch
{
name: 'auth-setup',
// testMatch không có → match tất cả *.spec.ts
dependencies: [],
}
// Đúng: testMatch giới hạn đúng file
{
name: 'auth-setup',
testMatch: /auth\.setup\.ts/,
// hoặc
testMatch: '**/*.setup.ts',
}
Pitfall 5 — Dùng --no-deps với artifact đã expire
--no-deps bỏ qua setup, giả định artifact còn hợp lệ. Nếu session trong storageState đã expire (token hết hạn, cookie bị clear), test fail với lỗi 401/403 hoặc redirect về trang login — thay vì lỗi setup rõ ràng.
Debug pattern: khi test fail với lỗi auth và bạn đang dùng --no-deps, xóa file storageState và chạy lại không có flag này để rebuild artifact.
# Xóa artifact cũ, chạy lại với setup
rm -rf playwright/.auth/
npx playwright test --project=main
Quiz
Câu 1. Config có 4 project: seed-db, auth-admin (depend seed-db), auth-user (depend seed-db), main (depend auth-admin, auth-user). Nếu auth-admin fail, những project nào bị skip? auth-user có tiếp tục chạy không?
Đáp án
Chỉ main bị skip với status expected-skipped vì main depend cả auth-admin lẫn auth-user, và auth-admin failed. auth-user vẫn chạy bình thường — nó depend seed-db (đã PASS), không depend auth-admin. Cascade skip chỉ theo chiều dependency, không lây ngang.
Câu 2. Bạn chạy npx playwright test --project=admin-tests --no-deps. admin-tests có dependencies: ['auth-admin'] và dùng storageState: 'playwright/.auth/admin.json'. File admin.json chưa tồn tại. Kết quả là gì?
Đáp án
Test fail với lỗi ENOENT: no such file or directory, open 'playwright/.auth/admin.json' (hoặc tương tự). --no-deps bỏ qua auth-admin hoàn toàn, không kiểm tra file artifact có tồn tại không. Playwright chỉ phát hiện file thiếu khi context được tạo và cố đọc storageState.
Câu 3. Tại sao dependencies được thiết kế là static (khai báo cứng trong config) thay vì dynamic (tính toán runtime)?
Đáp án
Playwright cần xây dựng toàn bộ dependency graph trước khi bắt đầu chạy bất kỳ test nào — để thực hiện topological sort, phát hiện circular dependency, và lập kế hoạch scheduling. Nếu dependency dynamic (tính lúc runtime), Playwright không thể biết graph đầy đủ ở bước lên kế hoạch. Dynamic dependency cũng làm phức tạp việc phát hiện circular dependency và khó predict kết quả CI.
Câu 4. Config có project-a depend project-b, project-b depend project-c, project-c depend project-a. Playwright sẽ báo lỗi ở bước nào — load config hay khi bắt đầu chạy test?
Đáp án
Lỗi xuất hiện ngay khi load config, trước khi bất kỳ test nào chạy. Playwright phân tích graph khi đọc playwright.config.ts và throw error với message liệt kê cycle: project-a → project-b → project-c → project-a. Đây là design đúng — fail fast thay vì để runner bị deadlock.
Câu 5. Bạn cần test trên cả staging và prod. Staging cần login, prod là public (không cần auth). Thiết kế config với dependencies như thế nào để --project=tests-staging tự động chạy auth setup, còn --project=tests-prod không cần setup?
Đáp án
Khai báo auth-staging setup project với testMatch: /auth-staging\.setup\.ts/. Project tests-staging có dependencies: ['auth-staging'] và storageState. Project tests-prod không có dependencies và không có storageState. Khi chạy --project=tests-staging, auth-staging tự động trigger. Khi chạy --project=tests-prod, không có setup nào chạy. Config auth-staging vẫn được khai báo trong projects nhưng chỉ chạy khi được depend hoặc filter trực tiếp.
