Học Playwright tiếng Việt, Cộng đồng Playwright cho người Việt

Random Q&A

[Q&A] Khi dùng Playwright TypeScript thì anh em quản lý user / password của từng môi trường như stag, uat ở đâu ạ Em tính nhét hết vô account_contants.ts để gọi ra?

Hỏi: Khi dùng Playwright TypeScript thì anh em quản lý user / password của từng môi trường như stag, uat ở đâu ạ Em tính nhét hết vô account_contants.ts để gọi ra?

Trả lời

Quản lý Credentials trong Playwright TypeScript cho nhiều môi trường

Việc quản lý username/password cho các môi trường khác nhau (staging, UAT, production) là một thách thức phổ biến khi viết automation test. Trong bài viết này, Playwright Việt Nam sẽ hướng dẫn các cách tiếp cận từ đơn giản đến nâng cao để giải quyết vấn đề này một cách an toàn và hiệu quả.

Vấn đề với việc hardcode credentials

Việc nhét tất cả credentials vào file account_constants.ts tuy đơn giản nhưng tiềm ẩn nhiều rủi ro:

  • Credentials bị lộ khi commit code lên repository
  • Khó thay đổi khi cần update password
  • Không linh hoạt khi chạy test trên nhiều môi trường

Các giải pháp được sử dụng

1. Sử dụng Environment Variables

Đây là cách phổ biến và an toàn nhất cho hầu hết các dự án.

Bước 1: Tạo file .env cho từng môi trường

# .env.staging
BASE_URL=https://staging.example.com
TEST_USERNAME=test_user_stg
TEST_PASSWORD=StrongPassword123!

# .env.uat
BASE_URL=https://uat.example.com
TEST_USERNAME=test_user_uat
TEST_PASSWORD=UatPassword456!

Bước 2: Cài đặt dotenv

npm install dotenv

Bước 3: Tạo config helper

// config/environment.config.ts
import * as dotenv from 'dotenv';
import path from 'path';

export type Environment = 'staging' | 'uat' | 'production';

export class EnvironmentConfig {
  private static instance: EnvironmentConfig;
  private currentEnv: Environment;

  private constructor() {
    this.currentEnv = (process.env.ENV as Environment) || 'staging';
    const envFile = `.env.${this.currentEnv}`;
    
    dotenv.config({
      path: path.resolve(process.cwd(), envFile)
    });
  }

  static getInstance(): EnvironmentConfig {
    if (!EnvironmentConfig.instance) {
      EnvironmentConfig.instance = new EnvironmentConfig();
    }
    return EnvironmentConfig.instance;
  }

  getCredentials() {
    return {
      username: process.env.TEST_USERNAME || '',
      password: process.env.TEST_PASSWORD || '',
      baseUrl: process.env.BASE_URL || ''
    };
  }

  getEnvironment(): Environment {
    return this.currentEnv;
  }
}

Bước 4: Sử dụng trong test

// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { EnvironmentConfig } from '../config/environment.config';

test.describe('Login Tests', () => {
  const config = EnvironmentConfig.getInstance();
  const credentials = config.getCredentials();

  test('should login successfully', async ({ page }) => {
    await page.goto(credentials.baseUrl);
    await page.fill('#username', credentials.username);
    await page.fill('#password', credentials.password);
    await page.click('button[type="submit"]');
    
    await expect(page).toHaveURL(/dashboard/);
  });
});

Bước 5: Chạy test với môi trường cụ thể

# Chạy với staging
ENV=staging npx playwright test

# Chạy với UAT
ENV=uat npx playwright test

2. Sử dụng Playwright Configuration

Playwright cho phép định nghĩa nhiều projects với config khác nhau.

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    {
      name: 'staging',
      use: {
        ...devices['Desktop Chrome'],
        baseURL: 'https://staging.example.com',
        storageState: 'auth/staging.json'
      },
    },
    {
      name: 'uat',
      use: {
        ...devices['Desktop Chrome'],
        baseURL: 'https://uat.example.com',
        storageState: 'auth/uat.json'
      },
    },
  ],
});

3. Sử dụng Authentication State

Để tránh login lặp lại trong mỗi test, sử dụng authentication state của Playwright.

// auth/auth.setup.ts
import { test as setup } from '@playwright/test';
import { EnvironmentConfig } from '../config/environment.config';

const config = EnvironmentConfig.getInstance();
const credentials = config.getCredentials();
const authFile = `auth/${config.getEnvironment()}.json`;

setup('authenticate', async ({ page }) => {
  await page.goto(credentials.baseUrl);
  await page.fill('#username', credentials.username);
  await page.fill('#password', credentials.password);
  await page.click('button[type="submit"]');
  
  await page.waitForURL(/dashboard/);
  
  // Lưu authentication state
  await page.context().storageState({ path: authFile });
});

4. Tích hợp với Secret Management Services

Cho các dự án enterprise, nên sử dụng các dịch vụ quản lý secrets chuyên nghiệp.

// config/secrets.manager.ts
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';

export class SecretsManager {
  private client: SecretsManagerClient;

  constructor() {
    this.client = new SecretsManagerClient({ region: 'us-east-1' });
  }

  async getCredentials(environment: string) {
    const command = new GetSecretValueCommand({
      SecretId: `playwright-credentials-${environment}`
    });

    try {
      const response = await this.client.send(command);
      return JSON.parse(response.SecretString || '{}');
    } catch (error) {
      console.error('Failed to retrieve secrets:', error);
      throw error;
    }
  }
}

Best Practices

  1. Không bao giờ commit credentials vào Git
    • Thêm .env* vào .gitignore
    • Sử dụng pre-commit hooks để kiểm tra
  2. Sử dụng credentials riêng cho automation
    • Tạo test accounts riêng biệt
    • Giới hạn quyền chỉ vừa đủ cho test
  3. Rotate credentials định kỳ
    • Thiết lập policy thay đổi password định kỳ
    • Sử dụng service accounts khi có thể
  4. Logging và Monitoring // utils/logger.ts export function logCredentialUsage(environment: string) { console.log(`Using credentials for environment: ${environment}`); // Không log password! }

Ví dụ hoàn chỉnh

Đây là một setup hoàn chỉnh kết hợp các best practices:

// config/test-config.ts
export interface TestConfig {
  baseUrl: string;
  credentials: {
    username: string;
    password: string;
  };
  timeout: number;
}

export class TestConfigManager {
  private static configs: Map<string, TestConfig> = new Map();

  static async loadConfig(environment: string): Promise<TestConfig> {
    if (this.configs.has(environment)) {
      return this.configs.get(environment)!;
    }

    const config: TestConfig = {
      baseUrl: process.env[`${environment.toUpperCase()}_BASE_URL`] || '',
      credentials: {
        username: process.env[`${environment.toUpperCase()}_USERNAME`] || '',
        password: process.env[`${environment.toUpperCase()}_PASSWORD`] || ''
      },
      timeout: 30000
    };

    this.validateConfig(config, environment);
    this.configs.set(environment, config);
    
    return config;
  }

  private static validateConfig(config: TestConfig, environment: string) {
    if (!config.baseUrl || !config.credentials.username || !config.credentials.password) {
      throw new Error(`Invalid configuration for environment: ${environment}`);
    }
  }
}

// tests/example.spec.ts
import { test } from '@playwright/test';
import { TestConfigManager } from '../config/test-config';

test.beforeEach(async ({ page }, testInfo) => {
  const env = process.env.TEST_ENV || 'staging';
  const config = await TestConfigManager.loadConfig(env);
  
  // Set base URL
  await page.goto(config.baseUrl);
  
  // Store config in test metadata
  testInfo.annotations.push({
    type: 'environment',
    description: env
  });
});

Tìm hiểu thêm

Việc quản lý credentials đúng cách không chỉ bảo vệ hệ thống của bạn mà còn giúp automation tests chạy ổn định và dễ maintain hơn. Hãy chọn phương pháp phù hợp với quy mô và yêu cầu bảo mật của dự án nhé ^^


Bạn có câu hỏi hoặc yêu cầu Playwright Việt Nam làm về nội dung gì? Xin mời nhắn cho chúng mình ở đây: https://voz.ee/s/BBA-community-request

Bạn muốn tham gia forum Automation Testing trên Facebook Messenger? Mời tham gia ở đây: https://voz.ee/s/BBA-FB-messenger-automation-group

Trả lời