Site logo

Modern BDD Test Automation with Playwright, Typescript

Hướng dẫn cài đặt và triển khai dự án Playwright với TypeScript từ A-Z

Giới thiệu về Playwright và TypeScript

Playwright là một framework testing end-to-end mạnh mẽ do Microsoft phát triển, cho phép thực hiện automated testing trên nhiều trình duyệt khác nhau như Chromium, Firefox và WebKit với một API thống nhất. Kết hợp với TypeScript, Playwright trở thành công cụ testing mạnh mẽ với khả năng type-checking, giúp code trở nên an toàn và dễ bảo trì hơn.

Trong bài viết này, chúng ta sẽ tìm hiểu cách cài đặt và cấu hình một dự án Playwright với TypeScript từ đầu. Chúng ta sẽ đi qua các bước chi tiết, bao gồm cài đặt các dependency cần thiết, tạo cấu trúc dự án, viết test cơ bản, và chạy các test case đầu tiên.

Tại sao nên chọn Playwright với TypeScript?

  • Cross-browser testing: Playwright hỗ trợ đầy đủ các trình duyệt chính (Chromium, Firefox, WebKit) với một API thống nhất.
  • Auto-waiting: Tự động đợi cho đến khi các element sẵn sàng để tương tác.
  • Type safety: TypeScript giúp phát hiện lỗi ngay trong quá trình phát triển.
  • Network interception: Khả năng chặn và sửa đổi các network request.
  • Mobile emulation: Hỗ trợ testing trên các thiết bị di động thông qua device emulation.
  • Parallel execution: Khả năng chạy test đồng thời để tối ưu thời gian.

Yêu cầu hệ thống

Trước khi bắt đầu, hãy đảm bảo máy tính của bạn đáp ứng các yêu cầu sau:

  • Node.js (phiên bản 14 hoặc cao hơn)
  • npm hoặc yarn (trình quản lý package)
  • Editor code như Visual Studio Code (khuyến nghị)

Để kiểm tra phiên bản Node.js đã cài đặt:

node -v

Để kiểm tra phiên bản npm:

npm -v

Cài đặt dự án Playwright với TypeScript

Bước 1: Tạo dự án mới

Đầu tiên, chúng ta cần tạo một thư mục mới cho dự án và khởi tạo một dự án Node.js:

mkdir awesome-playwright-typescript
cd awesome-playwright-typescript

Bước 2: Cài đặt Playwright và TypeScript

Cài đặt Playwright cùng với các dependencies cần thiết cho TypeScript:

npm init playwright@latest

Trong quá trình cài đặt, bạn sẽ được hỏi một số câu hỏi về cấu hình. Hãy chọn các tùy chọn phù hợp:

  • Do you want to use TypeScript or JavaScript? » TypeScript
  • Where to put your end-to-end tests? » tests
  • Add a GitHub Actions workflow? » Yes/No (tùy theo nhu cầu)
  • Install Playwright browsers (can be done manually via 'npx playwright install')? » Yes

Quá trình cài đặt sẽ tạo ra cấu trúc dự án ban đầu với các file cấu hình cần thiết.

Bước 3: Kiểm tra cấu trúc dự án

Sau khi cài đặt hoàn tất, cấu trúc dự án của bạn sẽ có dạng như sau:

playwright-typescript-project/
├── node_modules/
├── package.json
├── package-lock.json
├── playwright.config.ts
├── tests/
│   └── example.spec.ts
├── tests-examples/
│   └── demo-todo-app.spec.ts
└── tsconfig.json
  • ./tests/example.spec.ts: Đây là file test end-to-end mẫu.
  • ./tests-examples/demo-todo-app.spec.ts - Đây là file test end-to-end mẫu cho ứng dụng Todo.

Bước 4: Tìm hiểu các file cấu hình

File playwright.config.ts

File này chứa cấu hình chính cho Playwright, bao gồm các thiết lập về trình duyệt, timeout, và các tùy chọn khác:

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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {
    trace: 'on-first-retry',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

File tsconfig.json

File này chứa cấu hình TypeScript cho dự án:

{
  "compilerOptions": {
    "target": "ES2021",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "./dist",
    "strict": true,
    "noImplicitAny": true,
    "baseUrl": ".",
    "paths": {
      "*": ["node_modules/*"]
    },
    "esModuleInterop": true
  }
}

Viết test đầu tiên với Playwright và TypeScript

Để hiểu cách Playwright hoạt động, chúng ta sẽ viết một test đơn giản để kiểm tra trang chủ của Google.

Tạo file test mới

Trong thư mục tests, tạo một file mới với tên homepage.spec.ts:

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

test('has title', async ({ page }) => {
  await page.goto('https://www.google.com/');
  
  // Kiểm tra title của trang
  await expect(page).toHaveTitle(/Google/);
});

test('can search', async ({ page }) => {
  await page.goto('https://www.google.com/');
  
  // Nhập từ khóa vào ô tìm kiếm
  await page.locator('textarea[name="q"]').fill('Playwright');
  
  // Nhấn Enter để tìm kiếm
  await page.keyboard.press('Enter');
  
  // Đợi URL thay đổi và kiểm tra kết quả
  await expect(page).toHaveURL(/search\?q=Playwright/);
  await expect(page.locator('#search')).toContainText('Playwright');
});

Chạy test

Để chạy test, sử dụng lệnh:

npx playwright test

Để chạy test với UI mode:

npx playwright test --ui

Để chạy test trên một trình duyệt cụ thể:

npx playwright test --project=chromium

Để chạy test với một file cụ thể:

npx playwright test example

<!-- Auto generate tests with Codegen --> Để tự động tạo test với Playwright Codegen, sử dụng lệnh sau:

npx playwright codegen https://www.google.com

Đây là 1 tính năng khá hay của Playwright, giúp bạn tự động tạo mã test bằng cách ghi lại các thao tác của bạn trên trình duyệt. Bạn có thể chọn ngôn ngữ đầu ra là JavaScript hoặc TypeScript.

Cấu trúc dự án Playwright nâng cao

Để dự án dễ bảo trì và mở rộng, chúng ta nên tổ chức mã nguồn theo mô hình Page Object Model (POM).

Cấu trúc thư mục đề xuất

playwright-typescript-project/
├── node_modules/
├── package.json
├── playwright.config.ts
├── tsconfig.json
├── tests/
│   ├── example.spec.ts
│   ├── homepage.spec.ts
│   └── login.spec.ts
├── pages/
│   ├── BasePage.ts
│   ├── HomePage.ts
│   └── LoginPage.ts
├── fixtures/
│   ├── test-data.ts
│   └── auth.setup.ts
├── utils/
│   ├── helpers.ts
│   └── constants.ts
└── reports/

Tạo Page Object Model

Tạo thư mục pages và tạo file BasePage.ts:

import { Page, Locator } from '@playwright/test';

export class BasePage {
  readonly page: Page;
  
  constructor(page: Page) {
    this.page = page;
  }
  
  async navigateTo(url: string) {
    await this.page.goto(url);
  }
  
  async getTitle(): Promise<string> {
    return await this.page.title();
  }
  
  async waitForPageLoad() {
    await this.page.waitForLoadState('networkidle');
  }
}

Tạo file HomePage.ts:

import { Page, Locator } from '@playwright/test';
import { BasePage } from './BasePage';

export class HomePage extends BasePage {
  readonly searchInput: Locator;
  readonly searchButton: Locator;
  
  constructor(page: Page) {
    super(page);
    this.searchInput = page.locator('textarea[name="q"]');
    this.searchButton = page.locator('input[name="btnK"]');
  }
  
  async navigateToHomepage() {
    await this.navigateTo('https://www.google.com');
  }
  
  async search(keyword: string) {
    await this.searchInput.fill(keyword);
    await this.page.keyboard.press('Enter');
    await this.waitForPageLoad();
  }
}

Sử dụng Page Object trong test

Cập nhật file homepage.spec.ts để sử dụng Page Object:

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

test('Google search test using Page Object Model', async ({ page }) => {
  const homePage = new HomePage(page);
  
  // Điều hướng đến trang chủ
  await homePage.navigateToHomepage();
  
  // Kiểm tra title
  expect(await homePage.getTitle()).toContain('Google');
  
  // Thực hiện tìm kiếm
  await homePage.search('Playwright testing');
  
  // Kiểm tra URL sau khi tìm kiếm
  await expect(page).toHaveURL(/search\?q=Playwright\+testing/);
});

Cấu hình nâng cao cho Playwright

Thêm các tùy chọn trình duyệt

Cập nhật file playwright.config.ts để thêm các tùy chọn như headless mode, viewport size và làm chậm việc thực thi các thao tác:

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

export default defineConfig({
  testDir: './tests',
  timeout: 30000,
  expect: {
    timeout: 5000
  },
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: [['html'], ['list']],
  use: {
    baseURL: 'https://www.google.com',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'on-first-retry',
  },
  projects: [
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        viewport: { width: 1280, height: 720 },
        launchOptions: {
          slowMo: process.env.CI ? 0 : 100,
          headless: !!process.env.CI,
        }
      },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] },
    },
    {
      name: 'Mobile Safari',
      use: { ...devices['iPhone 12'] },
    },
  ],
});

Thiết lập môi trường test khác nhau

Tạo file .env.env.test để quản lý các biến môi trường:

# .env
BASE_URL=https://www.google.com
# .env.test
BASE_URL=https://staging.google.com

Cài đặt package dotenv:

npm install dotenv --save-dev

Cập nhật file playwright.config.ts để sử dụng biến môi trường:

import { defineConfig, devices } from '@playwright/test';
import dotenv from 'dotenv';

// Tải biến môi trường từ file .env
dotenv.config({
  path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env',
});

export default defineConfig({
  // ... các cấu hình khác
  use: {
    baseURL: process.env.BASE_URL,
    // ... các cấu hình khác
  },
});

Thực hành nâng cao với Playwright

Test API với Playwright

Playwright không chỉ dùng để test UI mà còn có thể test API:

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

test('API test example', async ({ request }) => {
  const response = await request.get('https://api.example.com/users');
  expect(response.ok()).toBeTruthy();
  
  const body = await response.json();
  expect(body).toHaveProperty('data');
  expect(Array.isArray(body.data)).toBeTruthy();
});

Screenshot và Video Recording

Thêm screenshot và video recording vào test:

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

test('Screenshot example', async ({ page }) => {
  await page.goto('https://www.google.com');
  
  // Chụp screenshot toàn trang
  await page.screenshot({ path: 'screenshots/google-homepage.png', fullPage: true });
  
  // Chụp screenshot một element cụ thể
  const searchBox = await page.locator('textarea[name="q"]');
  await searchBox.screenshot({ path: 'screenshots/search-box.png' });
  
  await expect(page).toHaveTitle(/Google/);
});

Sử dụng fixtures

Fixtures là một tính năng mạnh mẽ của Playwright, cho phép chia sẻ trạng thái giữa các test:

Tạo file fixtures/auth.fixture.ts:

import { test as base } from '@playwright/test';
import { HomePage } from '../pages/HomePage';
import { LoginPage } from '../pages/LoginPage';

// Định nghĩa interface cho fixtures
interface MyFixtures {
  homePage: HomePage;
  loginPage: LoginPage;
  loggedInPage: LoginPage;
}

// Mở rộng test với fixtures tùy chỉnh
export const test = base.extend<MyFixtures>({
  homePage: async ({ page }, use) => {
    const homePage = new HomePage(page);
    await homePage.navigateToHomepage();
    await use(homePage);
  },
  
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await loginPage.navigateToLoginPage();
    await use(loginPage);
  },
  
  loggedInPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await loginPage.navigateToLoginPage();
    await loginPage.login('test@example.com', 'password123');
    await use(loginPage);
  },
});

export { expect } from '@playwright/test';

Sử dụng fixtures trong test:

import { test, expect } from '../fixtures/auth.fixture';

test('Login test with fixture', async ({ loggedInPage }) => {
  // Test đã được đăng nhập từ fixture
  await expect(loggedInPage.welcomeMessage).toBeVisible();
  await expect(loggedInPage.welcomeMessage).toContainText('Welcome');
});

So sánh Playwright với các framework testing khác

Dưới đây là bảng so sánh Playwright với các framework testing phổ biến khác:

Tính năng Playwright Cypress Selenium Puppeteer
Hỗ trợ trình duyệt Chrome, Firefox, Safari, Edge Chrome, Firefox, Edge Tất cả trình duyệt phổ biến Chỉ Chrome/Chromium
Ngôn ngữ lập trình JavaScript, TypeScript, Python, .NET, Java JavaScript, TypeScript Java, C#, Python, Ruby, JS JavaScript, TypeScript
Auto-waiting Không Không
Parallel testing Có (với trả phí) Cần cấu hình thêm
Tracing & Debugging Rất mạnh Rất mạnh Cơ bản Cơ bản
Mobile testing Emulation Không có sẵn Có với Appium Không
Network interception Cần plugin
Community & Support Đang phát triển Lớn Rất lớn Trung bình
Độ ổn định Cao Cao Trung bình Cao

Tích hợp Playwright vào CI/CD

GitHub Actions

Tạo file .github/workflows/playwright.yml:

name: Playwright Tests
on:
  push:
    branches: [ main, master ]
  pull_request: