Biến môi trường là gì?
Biến môi trường (environment variables) là các giá trị được định nghĩa bên ngoài ứng dụng, có thể truy cập thông qua process.env
trong Node.js. Chúng giúp cấu hình ứng dụng mà không cần thay đổi code.
// Truy cập biến môi trường trong Node.js
console.log(process.env.NODE_ENV); // 'development' hoặc 'production'
console.log(process.env.DATABASE_URL); // 'mongodb://localhost:27017/myapp'
console.log(process.env.API_KEY); // 'sk-abc123xyz'
Biến môi trường có thể chứa bất kỳ thông tin nào: cấu hình database, API keys, port của server, môi trường chạy (development/production), và nhiều thông tin khác.
Tại sao cần biến môi trường?
1. Bảo mật thông tin nhạy cảm
Thay vì hard-code thông tin nhạy cảm trong code:
// KHÔNG NÊN - Credentials bị lộ trong code
const testUser = {
username: 'admin@example.com',
password: 'secretPassword123'
};
Sử dụng biến môi trường:
// NÊN - Thông tin nhạy cảm được giữ bên ngoài code
const testUser = {
username: process.env.TEST_USERNAME,
password: process.env.TEST_PASSWORD
};
2. Một code base – Nhiều môi trường
Cùng một code có thể chạy với cấu hình khác nhau cho development, staging, và production:
// playwright.config.js - Tự động adapt theo môi trường
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
headless: process.env.CI === 'true',
video: process.env.RECORD_VIDEO === 'true' ? 'on' : 'off',
screenshot: process.env.NODE_ENV === 'development' ? 'only-on-failure' : 'off'
},
// Development: BASE_URL=http://localhost:3000
// Staging: BASE_URL=https://staging.myapp.com
// Production: BASE_URL=https://myapp.com
});
3. Tuân thủ nguyên tắc 12-Factor App
Theo methodology 12-Factor App, cấu hình nên được tách biệt khỏi code. Điều này giúp ứng dụng dễ dàng deploy và scale trên các nền tảng khác nhau (Heroku, AWS, Docker).
4. Dễ dàng thay đổi cấu hình
Không cần sửa code và rebuild khi muốn đổi port, database URL, hay bật/tắt feature:
// test/e2e/login.spec.js
import { test, expect } from '@playwright/test';
test('login flow', async ({ page }) => {
// Thay đổi URL mà không cần sửa code
await page.goto(process.env.BASE_URL);
// Bật/tắt feature qua biến môi trường
if (process.env.ENABLE_2FA === 'true') {
await page.fill('[data-testid="2fa-code"]', process.env.TEST_2FA_CODE);
}
// Timeout có thể config từ bên ngoài
await page.waitForSelector('.dashboard', {
timeout: parseInt(process.env.TIMEOUT) || 30000
});
});
Các cách triển khai biến môi trường
Có hai cách chính để set biến môi trường trong Node.js:
- Set trực tiếp từ command line – Phù hợp cho các biến tạm thời hoặc scripts
- Sử dụng file .env – Phù hợp cho quản lý nhiều biến cấu hình
Set trực tiếp từ command line
Cú pháp cơ bản (khác nhau theo OS)
Trên Mac/Linux:
BROWSER=firefox BASE_URL=https://staging.myapp.com npx playwright test
Trên Windows (Command Prompt):
SET BROWSER=firefox
SET BASE_URL=https://staging.myapp.com
npx playwright test
Trên Windows (PowerShell):
$env:BROWSER="firefox"
$env:BASE_URL="https://staging.myapp.com"
npx playwright test
Vấn đề: Không nhất quán giữa các hệ điều hành
Khi làm việc trong team hoặc deploy lên server, bạn gặp vấn đề:
// package.json
{
"scripts": {
// Script này chỉ chạy được trên Mac/Linux
"test:staging": "BASE_URL=https://staging.myapp.com npx playwright test",
// Developer dùng Windows không chạy được!
// Họ phải tạo script riêng
"test:staging-windows": "SET BASE_URL=https://staging.myapp.com && npx playwright test"
}
}
Điều này dẫn đến:
- Code không portable (không chạy được mọi nơi)
- Team phải maintain nhiều scripts
- CI/CD phức tạp khi server Linux nhưng dev dùng Windows
Giải pháp: Cross-env
Cross-env là package giúp set biến môi trường với cú pháp thống nhất trên mọi hệ điều hành.
Cài đặt:
npm install --save-dev cross-env
Sử dụng trong package.json:
{
"scripts": {
// Một script duy nhất - chạy được trên Windows/Mac/Linux
"test": "cross-env BASE_URL=http://localhost:3000 npx playwright test",
"test:staging": "cross-env BASE_URL=https://staging.myapp.com HEADLESS=true npx playwright test",
"test:prod": "cross-env BASE_URL=https://myapp.com HEADLESS=true npx playwright test",
"test:debug": "cross-env PWDEBUG=1 npx playwright test",
"test:headed": "cross-env HEADLESS=false SLOW_MO=1000 npx playwright test"
}
}
Cách cross-env hoạt động:
Khi bạn chạy npm start
, cross-env tự động chuyển đổi:
# Bạn viết
cross-env BASE_URL=https://staging.myapp.com npx playwright test
# Cross-env chuyển thành:
# - Trên Windows: SET BASE_URL=https://staging.myapp.com && npx playwright test
# - Trên Mac/Linux: BASE_URL=https://staging.myapp.com npx playwright test
Ví dụ thực tế:
// playwright.config.js
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
// Đọc biến từ cross-env
testDir: './tests',
timeout: parseInt(process.env.TIMEOUT) || 30000,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
headless: process.env.HEADLESS !== 'false',
screenshot: process.env.SCREENSHOT || 'only-on-failure',
video: process.env.VIDEO || 'retain-on-failure',
trace: process.env.TRACE || 'on-first-retry',
// Slow motion để debug
launchOptions: {
slowMo: process.env.SLOW_MO ? parseInt(process.env.SLOW_MO) : 0
}
},
// Browser config từ biến môi trường
projects: process.env.BROWSER ? [
{ name: process.env.BROWSER, use: { ...devices[process.env.BROWSER] } }
] : [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } }
]
});
Sử dụng file .env
Khi có nhiều biến môi trường, việc set từ command line trở nên cồng kềnh. File .env
giúp quản lý tập trung tất cả biến cấu hình.
Dotenv – Package phổ biến nhất
Cài đặt:
npm install dotenv
Tạo file .env ở root project:
# .env
NODE_ENV=development
BASE_URL=http://localhost:3000
# Test Accounts
TEST_USERNAME=testuser@example.com
TEST_PASSWORD=Test123!@#
ADMIN_USERNAME=admin@example.com
ADMIN_PASSWORD=Admin456$%^
# API Configuration
API_BASE_URL=http://localhost:3001/api
API_KEY=test-api-key-abc123
API_SECRET=test-api-secret-xyz789
# Test Configuration
HEADLESS=false
SLOW_MO=0
TIMEOUT=30000
SCREENSHOT=only-on-failure
VIDEO=off
TRACE=on-first-retry
# Feature Flags
ENABLE_AUTH_TESTS=true
ENABLE_PAYMENT_TESTS=false
SKIP_FLAKY_TESTS=true
Sử dụng trong file config:
// playwright.config.js
import { defineConfig } from '@playwright/test';
import dotenv from 'dotenv';
// Load biến từ file .env
dotenv.config();
// Hoặc load file .env khác theo môi trường
dotenv.config({
path: `.env.${process.env.NODE_ENV || 'development'}`
});
export default defineConfig({
use: {
baseURL: process.env.BASE_URL,
headless: process.env.HEADLESS === 'true',
screenshot: process.env.SCREENSHOT,
video: process.env.VIDEO,
trace: process.env.TRACE
},
// Skip flaky tests nếu cần
grep: process.env.SKIP_FLAKY_TESTS === 'true' ? /@stable/ : undefined,
grepInvert: process.env.SKIP_FLAKY_TESTS === 'true' ? /@flaky/ : undefined
});
Sử dụng trong test file
// playwright.config.js
import { defineConfig } from '@playwright/test';
import dotenv from 'dotenv';
// Load biến từ file .env
dotenv.config();
// Hoặc load file .env khác theo môi trường
dotenv.config({
path: `.env.${process.env.NODE_ENV || 'development'}`
});
export default defineConfig({
use: {
baseURL: process.env.BASE_URL,
headless: process.env.HEADLESS === 'true',
screenshot: process.env.SCREENSHOT,
video: process.env.VIDEO,
trace: process.env.TRACE
},
// Skip flaky tests nếu cần
grep: process.env.SKIP_FLAKY_TESTS === 'true' ? /@stable/ : undefined,
grepInvert: process.env.SKIP_FLAKY_TESTS === 'true' ? /@flaky/ : undefined
});
Best practices với dotenv:
- Tạo file .env.example:
# .env.example - Commit file này lên git
NODE_ENV=development
BASE_URL=
TEST_USERNAME=
TEST_PASSWORD=
API_KEY=
HEADLESS=true
- Thêm .env vào .gitignore:
# .gitignore
.env
.env.local
.env.staging
.env.production
- Validate biến môi trường:
// config/validate.js
const required = [
'BASE_URL',
'TEST_USERNAME',
'TEST_PASSWORD'
];
for (const variable of required) {
if (!process.env[variable]) {
throw new Error(`Missing required environment variable: ${variable}`);
}
}
- Sử dụng nhiều file .env:
// playwright.config.js
import dotenv from 'dotenv';
// Load file .env theo môi trường
const envFile = process.env.TEST_ENV === 'production'
? '.env.production'
: process.env.TEST_ENV === 'staging'
? '.env.staging'
: '.env.development';
dotenv.config({ path: envFile });
console.log(`Loaded config from ${envFile}`);
console.log(`Testing against: ${process.env.BASE_URL}`);
Dotenvx – Phiên bản nâng cao
Dotenvx là version mới với nhiều tính năng bổ sung, đặc biệt là mã hóa file .env.
Cài đặt:
npm install @dotenvx/dotenvx
Tính năng nổi bật:
- Mã hóa file .env:
# Mã hóa file .env thành .env.vault
npx dotenvx encrypt
# Chạy tests với file đã mã hóa
DOTENV_KEY=your_key npx dotenvx run -- npx playwright test
- Hỗ trợ nhiều môi trường:
# Tạo các file .env cho từng môi trường
.env.development # Local testing
.env.staging # Staging tests
.env.production # Production smoke tests
# Tự động load đúng file
npx dotenvx run --env-file=.env.staging -- npx playwright test
- Validate và type safety:
// config/env.schema.js
const schema = {
BASE_URL: { type: 'string', required: true, pattern: '^https?://' },
TEST_USERNAME: { type: 'string', required: true, pattern: '^.+@.+$' },
TIMEOUT: { type: 'number', default: 30000 },
HEADLESS: { type: 'boolean', default: true }
};
Kết hợp cross-env và dotenv
Trong thực tế, hai công cụ này thường được dùng cùng nhau:
// package.json
{
"scripts": {
// cross-env override biến cụ thể, dotenv load các biến mặc định
"test": "npx playwright test",
"test:chrome": "cross-env BROWSER=chromium npx playwright test",
"test:firefox": "cross-env BROWSER=firefox npx playwright test",
"test:headed": "cross-env HEADLESS=false npx playwright test",
"test:staging": "cross-env TEST_ENV=staging npx playwright test",
"test:ci": "cross-env CI=true HEADLESS=true npx playwright test"
}
}
// playwright.config.js
import { defineConfig } from '@playwright/test';
import dotenv from 'dotenv';
// Load dotenv dựa trên TEST_ENV từ cross-env
const envFile = `.env.${process.env.TEST_ENV || 'development'}`;
dotenv.config({ path: envFile });
console.log('Environment:', process.env.TEST_ENV || 'development');
console.log('Testing URL:', process.env.BASE_URL);
console.log('Headless:', process.env.HEADLESS !== 'false');
export default defineConfig({
use: {
baseURL: process.env.BASE_URL,
headless: process.env.HEADLESS !== 'false'
}
});
Best Practices tổng hợp
- Không bao giờ commit file .env chứa thông tin nhạy cảm
- Luôn có file .env.example để hướng dẫn team
- Validate biến môi trường khi khởi động app
- Dùng cross-env cho npm scripts để đảm bảo cross-platform
- Tổ chức biến môi trường theo nhóm trong file .env
- Sử dụng naming convention nhất quán (UPPER_SNAKE_CASE)
- Document rõ ràng ý nghĩa của từng biến
- Tạo helper functions để đọc và parse biến môi trường
// helpers/env.js
export const getConfig = () => ({
baseURL: process.env.BASE_URL || 'http://localhost:3000',
credentials: {
username: process.env.TEST_USERNAME,
password: process.env.TEST_PASSWORD
},
timeout: parseInt(process.env.TIMEOUT) || 30000,
isCI: process.env.CI === 'true',
isHeadless: process.env.HEADLESS !== 'false'
});
Biến môi trường là công cụ không thể thiếu trong automation testing với Playwright, giúp test suite linh hoạt, bảo mật và dễ maintain trên nhiều môi trường khác nhau.
Trả lời