Playwright Test

Neben seiner Funktion als Browser-Automatisierungswerkzeug verfügt Playwright außerdem über einvollständiges Test-Framework —— Playwright Test。

Playwright Test hilft uns, Testfälle zu organisieren, Berichte zu generieren, parallel auszuführen, bei Fehlern erneut zu versuchen, und ist von Haus aus voll funktionsfähig.

  1. Playwright Test ist einintegriertes Test-Framework, ohne zusätzliche Abhängigkeiten.
  2. Die Konfigurationsdatei kannTimeout, Wiederholungen, Berichte, Browser-Projekteusw. festlegen.
  3. Testfälle unterstützenGruppierung, Hooks, datengetriebene Tests。
  4. und bietenparallele Ausführung, HTML-Berichte, Wiederholungen bei Fehlernund weitere fortgeschrittene Funktionen.

Überblick über das Test-Framework

  • Integriertes Test-Framework: enthält einen integrierten Test-Runner, Jest oder Mocha müssen nicht separat installiert werden.
  • Integrierte Assertion-Bibliothek: unterstütztexpectAssertions.
  • Testisolierung: Jedertestläuft standardmäßig in einem neuen Browser-Kontext, unabhängig voneinander.
  • Umfangreiche Funktionen: parallele Ausführung, Wiederholungen, Screenshots, Aufzeichnung, HTML-Berichte, usw.

Installationsmethode:

npm init playwright@latest

Projektstruktur automatisch generieren, einschließlich Konfigurationsdateiplaywright.config.js。

1. Details zur Konfigurationsdatei

Playwright Test verwendetplaywright.config.js(oder.ts) als Einstiegspunkt für die Konfiguration.

// playwright.config.js
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',         // 测试目录
  timeout: 30 * 1000,         // 单个测试超时时间
  retries: 2,                 // 失败重试次数
  reporter: [['html'], ['list']], // 测试报告类型
  use: {
    headless: true,           // 是否无头模式
    screenshot: 'only-on-failure', // 失败时截图
    video: 'retain-on-failure',    // 失败时保留视频
    baseURL: 'https://example.com', // 基础 URL
  },
  projects: [
    { name: 'Chromium', use: { browserName: 'chromium' } },
    { name: 'Firefox',  use: { browserName: 'firefox' } },
    { name: 'WebKit',   use: { browserName: 'webkit' } },
  ],
});

2. Struktur des Testskripts

Playwright Test-Skripte enthalten normalerweise:

1. Testbibliothek importieren

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

2. Testfälle schreiben

test('示例测试', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle('Example Domain');
});

3. Gruppierung und Hooks

  • Verwenden Siedescribezum Organisieren von Tests
  • Verwenden SiebeforeEach / afterEachfür Setup und Teardown

Schreiben von Testfällen

1. test()Funktionsverwendung

test('页面标题验证', async ({ page }) => {
  await page.goto('https://playwright.dev');
  await expect(page).toHaveTitle(/Playwright/);
});

2. describe()Gruppierung

test.describe('用户登录模块', () => {
  test('输入正确账号密码', async ({ page }) => {
    // 登录逻辑...
  });

  test('输入错误密码', async ({ page }) => {
    // 验证错误提示...
  });
});

3. Test-Hooks

test.describe('购物车模块', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://example.com/cart');
  });

  test.afterEach(async ({ page }) => {
    await page.screenshot({ path: 'cart.png' });
  });

  test('添加商品', async ({ page }) => {
    await page.click('#add-item');
    await expect(page.locator('#cart-count')).toHaveText('1');
  });
});

4. Testdatenvorbereitung

Unterstützt parametrisierte Tests:

const users = [
  { name: 'admin', password: '123456' },
  { name: 'guest', password: 'guest' },
];

for (const user of users) {
  test(`用户 ${user.name} 登录`, async ({ page }) => {
    await page.goto('https://example.com/login');
    await page.fill('#username', user.name);
    await page.fill('#password', user.password);
    await page.click('#submit');
    await expect(page.locator('.welcome')).toBeVisible();
  });
}

Ausführung und Berichterstattung

1. Testausführungsbefehle

npx playwright test          # 运行全部测试
npx playwright test login.spec.js   # 运行单个文件
npx playwright test -g "登录"       # 运行指定用例

2. Paralleles Testen

Playwright standardmäßigführt Dateien parallel aus, oder du kannst die Anzahl der Worker in der Konfigurationsdatei angeben:

npx playwright test --workers=4

3. Testberichterstellung

Nach der AusführungHTML-Bericht generieren:

npx playwright show-report

4. Wiederholungsmechanismus bei Fehlschlägen

In der Konfigurationsdateiplaywright.config.jsfestlegen:

retries: 2

Wenn ein Test fehlschlägt, wird er automatisch wiederholt, geeignet für den Umgang mit sporadischen Fehlern.

  1. Playwright Test ist einintegriertes Test-Framework, keine zusätzlichen Abhängigkeiten erforderlich.
  2. Die Konfigurationsdatei kannTimeout, Wiederholungen, Berichte, Browser-Projekteusw. festlegen.
  3. Testfälle unterstützenGruppierung, Hooks, datengetriebene Tests。
  4. und bietenparallele Ausführung, HTML-Berichte, Wiederholungen bei Fehlernund weitere fortgeschrittene Funktionen.

Beispiel für eine vollständige Testsuite

1. Projektstruktur

playwright-demo/
├─ playwright.config.ts           # 全局配置(多项目、多浏览器、报告、重试…)
├─ package.json
├─ tests/
│  ├─ auth.setup.ts               # 一次性登录,生成 storageState
│  ├─ smoke/
│  │  └─ home.spec.ts             # 冒烟用例:标题/URL/截图对比
│  └─ e2e/
│     └─ cart.spec.ts             # 端到端:添加购物车、断言、附件、步骤
├─ fixtures/
│  ├─ pages.ts                    # 页面对象(PO)
│  └─ test.extend.ts              # 自定义 fixtures
└─ snapshots/                     # 视觉基线(自动生成)

2. Konfigurationsdatei (playwright.config.ts)

Beispiel

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

export default defineConfig({
  testDir: './tests',
  timeout: 30_000,
  retries: 2,
  workers: 4,
  reporter: [['html', { outputFolder: 'report' }], ['list']],
  forbidOnly: !!process.env.CI,

  // Einheitliche Standardverwendung (kann auf Projekt- oder Testebene überschrieben werden)
  Verwenden Sie: {
    baseURL: 'https://demo.playwright.dev',
    headless: true,
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    trace: 'retain-on-failure',
    viewport: { width: 1366, height: 900 },
    locale: 'zh-CN',
  },

  // Mehrere Projekte: verschiedene Browser/Geräte/Login-Zustände
  projects: [
    { name: 'Chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'Firefox',  use: { ...devices['Desktop Firefox'] } },
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 7'], headless: wahr },
    },
    // Projekt mit Login-Zustand (storageState wiederverwenden)
    {
      name: 'Chromium Logged-in',
      use: { storageState: 'storageState.json' },
      dependencies: ['setup'], // Hängt von einem Setup-Projekt ab, um den Login-Zustand zu generieren
    },
    // Setup-Projekt: wird einmal ausgeführt, um storageState zu generieren
    {
      name: 'setup',
      testMatch: /auth\.setup\.ts/,
    },
  ],
});

Wichtige Punkte:Projekte ermöglichen, dass dieselbe Testsuite in verschiedenen Browsern/Konfigurationen ausgeführt wird; use kann auf globaler/Projekt-/Testebene überschrieben werden; Berichte und Wiederholungsversuche werden zentral in der Konfiguration verwaltet.

3. Fixtures und Page Objects

fixtures/test.extend.ts (benutzerdefinierte Fixtures + Kapselung gemeinsamer Schritte)

Beispiel

// fixtures/test.extend.ts
import { test as base } from '@playwright/test';
import { HomePage, CartPage } from './pages';

type MyFixtures = {
  home: HomePage;
  cart: CartPage;
};

export const test = base.extend<MyFixtures>({
  home: async ({ page }, use) => {
    const home = new HomePage(page);
    await use(home);
  },
  cart: async ({ page }, use) => {
    const cart = new CartPage(page);
    await use(cart);
  },
});

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

Fügen Sie benutzerdefinierte Fixtures basierend auf test.extend() hinzu, sodass sich home- und cart-Instanzen in jedem Testfall direkt injizieren lassen.

fixtures/pages.ts (Page Objects)

Beispiel

// fixtures/pages.ts
import { Page, expect } from '@playwright/test';

export class HomePage {
  constructor(private page: Page) {}
  async goto() { await this.page.goto('/'); }
  get tryTodoLink() { return this.page.getByRole('link', { name: /ToDo MVC/i }); }
}

export class CartPage {
  constructor(private page: Page) {}
  async goto() { await this.page.goto('/e2e'); } // Angenommen, eine e2e-Beispielseite existiert
  get addFirstItem() { return this.page.getByRole('button', { name: /Add .* #1/i }); }
  get cartCount() { return this.page.locator('[data-test=cart-count]'); }
  async addOne() { await this.addFirstItem.click(); }
  async expectCount(n: number) { await expect(this.cartCount).toHaveText(String(n)); }
}

4. Vorab-Anmeldung (storageState einmal generieren)

tests/auth.setup.ts (läuft nur im Projekt "setup")

Beispiel

// tests/auth.setup.ts
import { test, expect } from '@playwright/test';

test('create storageState', async ({ page }) => {
  await page.goto('https://demo.playwright.dev/todomvc');
  // ...Ersetzen Sie durch Ihren Login-Ablauf...
  // Angenommen, die Anmeldung ist erfolgreich
  await expect(page).toHaveURL(/todomvc/);
  await page.context().storageState({ path: 'storageState.json' });
});

5. Smoke-Tests (Titel/URL/Visueller Vergleich)

tests/smoke/home.spec.ts

Beispiel

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

test.describe('Smoke - Home', () => {
  test('title & url & visual snapshot', async ({ page }) => {
    await page.goto('https://playwright.dev');
    await expect(page).toHaveTitle(/Playwright/);
    await expect(page).toHaveURL(/playwright\.dev/);

    // Visuelle Regression (Basislinie automatisch generieren/vergleichen)
    await expect(page).toHaveScreenshot(); // Der erste Durchlauf erzeugt eine Basislinie
  });
});

Für den visuellen Vergleich wird empfohlen, toHaveScreenshot zu verwenden (bevorzugt gegenüber dem manuellen Ansatz mit page.screenshot() + toMatchSnapshot).

6. End-to-End-Tests (Schritte, Anhänge, Assertions)

tests/e2e/cart.spec.ts

Beispiel

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

test.describe.configure({ mode: 'parallel' });

test.describe('E2E - Warenkorb', () => {
  test.beforeEach(async ({ home }) => {
    await test.step('Zur Startseite gehen', async () => {
      await home.goto();
    });
  });

  test('Produkt zum Warenkorb hinzufügen (mit Anhang und Assertion)', async ({ page, cart }) => {
    await test.step('Zur Warenkorbseite gehen und Produkt hinzufügen', async () => {
      await cart.goto();
      await cart.addOne();
      await cart.expectCount(1);
    });

    await test.step('Debug-Informationen zum Bericht hinzufügen', async () => {
      await test.info().attach('state.json', {
        contentType: 'application/json',
        body: Buffer.from(JSON.stringify({ ts: Date.now(), note: 'nach dem Hinzufügen' })),
      });
    });

    // Screenshot auf Elementebene
    await test.step('Element-Screenshot', async () => {
      const badge = page.locator('[data-test=cart-count]');
      await expect(badge).toBeVisible();
      await expect(badge).toHaveScreenshot(); // Element-Snapshot
    });
  });
});

Verwenden Sie test.step, um Schritte hierarchisch aufzuzeichnen, und verwenden Sie test.info().attach(), um Debug-Daten/Screenshots zur einfacheren Fehlersuche an den Bericht anzuhängen; im Parallelmodus kann describe.configure({ mode: 'parallel' }) verwendet werden.

7. Ausführung und Berichterstattung

Gängige Befehle:

npx playwright test                # 运行全部
npx playwright test tests/e2e      # 运行目录
npx playwright test -g "Cart"      # 按标题过滤
npx playwright test --project="Chromium"   # 指定项目
npx playwright show-report         # 打开HTML报告
npx playwright test --update-snapshots     # 更新视觉基线
npx playwright test --debug        # 调试模式(Inspector)

Informationen zur CLI-Filterung, Projektauswahl, Berichtsanzeige, Snapshot-Aktualisierungen usw. finden Sie in der offiziellen CLI-/Reporting-/Snapshot-Dokumentation.


Kurzreferenz für häufig verwendete zugehörige APIs (Playwright Test)

1. Tests und Gruppierung

API Zweck
test(name, fn) Tests definieren
test.describe(title, fn) Gruppierung
test.describe.configure({ mode }) parallel / serial
test.beforeAll/afterAll Before-/After-Hooks auf Gruppenebene
test.beforeEach/afterEach Before-/After-Hooks für Testfälle
test.skip/only/fixme/slow/fail Testfälle markieren/steuern

2. Fixtures und Konfiguration

API Zweck
test.extend<Fixtures>(defs) Benutzerdefinierte Fixtures erweitern
test.use(options) Überschreibung auf TestebeneuseOptionen (wie z. B.storageState、viewport、localeusw.)
defineConfig({...}) Eintrag in der Konfigurationsdatei (testDir、retries、workers、reporter…)
projects Ausführung von Multi-Projekt-/Multi-Browser-/verschiedenen Konfigurationen
use: { baseURL, trace, video, screenshot, viewport, locale, timezoneId, storageState } Laufzeitumgebung und Aufnahmestrategie

Details zu Fixtures / Verwendung / Konfigurationsoptionen und Projekten finden Sie in der offiziellen Dokumentation "Fixtures / Use options / Configuration / Projects". (Playwright)

3. Assertions (expect)

API Zweck
expect(value).toBe / toEqual / toContain ... Allgemeine Assertions
expect(locator).toHaveText/Value/Attribute Webspezifische Assertions (mit intelligentem Warten)
expect(locator).toBeVisible/Hidden/Enabled/Disabled/Checked Zustands-Assertions
expect(page).toHaveTitle/URL Seiten-Assertions
`expect(page/locator).toHaveScreenshot([name options])` Visueller Snapshot-Vergleich (empfohlen)

Empfohlene Vorgehensweisen für Assertions und visuelle Snapshots finden Sie in der Dokumentation zu Assertions und Snapshots.(Playwright)

4. Schritte und Anhänge

API Zweck
test.step(name, fn) Tests in Schritte unterteilen (Visualisierung im Bericht)
`test.info().attach(name, { path body, contentType })` Dateien/Daten an Tests oder Schritte anhängen (im Bericht angezeigt)

Anhänge können auch auf Schritt-Ebene angewendet werden (siehe TestStep / TestStepInfo).(Playwright)

5. Berichterstellung und CLI

Befehl/Konfiguration Zweck
reporter: [['html', { outputFolder }], ['list']] Berichtskonfiguration
npx playwright test --project="Chromium" Projekt zum Ausführen auswählen
-g "keyword" / --grep / --grep-invert Testfälle filtern
--update-snapshots Visuelle Baselines aktualisieren
npx playwright show-report Bericht öffnen

Siehe die offizielle Dokumentation für die vollständigen CLI- und Reporter-Optionen.(Playwright)

Weitere Erweiterungen