Files
novalon-website/docs/testing/testing.md
T

13 KiB

测试文档

测试概述

项目使用 Playwright 进行端到端(E2E)测试,测试框架位于 e2e/ 目录,采用 Page Object 模式组织测试代码。

测试框架结构

e2e/
├── src/
│   ├── config/              # 配置文件
│   │   ├── environments.ts  # 环境配置
│   │   └── network-configs.ts # 网络配置
│   ├── data/                # 测试数据
│   │   └── test-data.ts
│   ├── fixtures/            # 测试 Fixtures
│   │   ├── base.fixture.ts  # 基础 Fixture
│   │   └── a11y.fixture.ts  # 可访问性 Fixture
│   ├── pages/               # Page Object Model
│   │   ├── BasePage.ts      # 基础页面
│   │   ├── HomePage.ts      # 首页
│   │   ├── AboutPage.ts     # 关于页
│   │   ├── CasesPage.ts     # 案例页
│   │   ├── ContactPage.ts   # 联系页
│   │   ├── NewsPage.ts      # 新闻页
│   │   ├── ProductsPage.ts  # 产品页
│   │   ├── ServicesPage.ts  # 服务页
│   │   └── SolutionsPage.ts # 解决方案页
│   └── tests/               # 测试用例
│       ├── accessibility/   # 可访问性测试
│       ├── debug/           # 调试测试
│       ├── deployment/      # 部署就绪测试
│       ├── error-handling/  # 错误处理测试
│       ├── mobile/          # 移动端测试
│       ├── performance/     # 性能测试
│       ├── regression/      # 回归测试
│       ├── responsive/      # 响应式测试
│       ├── security/        # 安全测试
│       ├── smoke/           # 冒烟测试
│       ├── utils/           # 工具测试
│       └── visual/          # 视觉回归测试
├── playwright.config.ts     # Playwright 配置
├── package.json
└── .env.example

测试类型

1. 冒烟测试 (Smoke Tests)

目录: e2e/src/tests/smoke/

目的: 验证核心功能是否正常工作

测试文件:

  • all-pages.spec.ts - 所有页面加载测试
  • home-page.smoke.spec.ts - 首页冒烟测试
  • contact-page.smoke.spec.ts - 联系页冒烟测试
  • navigation.smoke.spec.ts - 导航冒烟测试

运行命令:

npm run test:smoke
# 或
npx playwright test --grep @smoke

2. 回归测试 (Regression Tests)

目录: e2e/src/tests/regression/

目的: 确保新代码没有破坏现有功能

测试文件:

  • contact-form.regression.spec.ts - 联系表单回归测试
  • home-page.regression.spec.ts - 首页回归测试
  • navigation.spec.ts - 导航回归测试

运行命令:

npm run test:regression
# 或
npx playwright test --grep @regression

3. 性能测试 (Performance Tests)

目录: e2e/src/tests/performance/

目的: 验证页面性能指标

测试文件:

  • core-web-vitals.spec.ts - Core Web Vitals 测试
  • performance.spec.ts - 通用性能测试
  • image-loading.spec.ts - 图片加载性能
  • interaction-performance.spec.ts - 交互性能测试

监控指标:

  • LCP (Largest Contentful Paint) < 2.5s
  • FID (First Input Delay) < 100ms
  • CLS (Cumulative Layout Shift) < 0.1
  • TTFB (Time to First Byte) < 600ms

运行命令:

npm run test:performance
# 或
npx playwright test --grep @performance

4. 响应式测试 (Responsive Tests)

目录: e2e/src/tests/responsive/

目的: 验证多设备适配

测试文件:

  • responsive.spec.ts - 响应式布局测试
  • mobile-interaction.spec.ts - 移动端交互测试

测试设备:

  • Desktop: 1920x1080, 1366x768
  • Tablet: iPad Pro (1024x1366), iPad Air (820x1180)
  • Mobile: iPhone 12 (390x844), iPhone SE (375x667), Pixel 5 (393x851)

运行命令:

npm run test:responsive
# 或
npx playwright test --grep @responsive

5. 可访问性测试 (Accessibility Tests)

目录: e2e/src/tests/accessibility/

目的: 验证 WCAG 合规性

测试文件:

  • accessibility.spec.ts - 可访问性测试
  • wcag-compliance.spec.ts - WCAG 合规测试

检查项:

  • 颜色对比度 ≥ 4.5:1 (AA 级别)
  • 键盘导航支持
  • 屏幕阅读器兼容
  • ARIA 属性正确性
  • 焦点顺序合理

运行命令:

npm run test:accessibility
# 或
npx playwright test --grep @accessibility

6. 安全测试 (Security Tests)

目录: e2e/src/tests/security/

目的: 验证安全防护措施

测试文件:

  • security.spec.ts - 通用安全测试
  • xss-protection.spec.ts - XSS 防护测试
  • csrf-protection.spec.ts - CSRF 防护测试

检查项:

  • XSS 攻击防护
  • CSRF Token 验证
  • 安全头部配置
  • 表单验证

运行命令:

npm run test:security
# 或
npx playwright test --grep @security

7. 视觉回归测试 (Visual Tests)

目录: e2e/src/tests/visual/

目的: 检测 UI 变化

测试文件:

  • home-page.visual.spec.ts - 首页视觉测试
  • contact-page.visual.spec.ts - 联系页视觉测试
  • visual-regression.spec.ts - 视觉回归测试

快照目录:

  • *-snapshots/ - 基线快照

运行命令:

npm run test:visual
# 或
npx playwright test --grep @visual

更新快照:

npx playwright test --grep @visual --update-snapshots

8. 移动端测试 (Mobile Tests)

目录: e2e/src/tests/mobile/

目的: 验证移动端功能

测试文件:

  • compatibility/mobile-compatibility.spec.ts - 兼容性测试
  • gesture/mobile-gesture.spec.ts - 手势测试
  • network/network-environment.spec.ts - 网络环境测试
  • performance/mobile-performance.spec.ts - 移动性能测试
  • pwa/pwa-functionality.spec.ts - PWA 功能测试
  • mobile-ux.spec.ts - 移动端 UX 测试

9. 部署就绪测试 (Deployment Tests)

目录: e2e/src/tests/deployment/

目的: 验证部署前检查

测试文件:

  • deployment-readiness.spec.ts - 部署就绪检查
  • quick-check.spec.ts - 快速检查

Page Object Model

基础页面类

// e2e/src/pages/BasePage.ts
export class BasePage {
  readonly page: Page;

  constructor(page: Page) {
    this.page = page;
  }

  async navigate(path: string) {
    await this.page.goto(path);
  }

  async waitForPageLoad() {
    await this.page.waitForLoadState('networkidle');
  }

  async takeScreenshot(name: string) {
    await this.page.screenshot({ path: `screenshots/${name}.png` });
  }
}

首页 Page Object

// e2e/src/pages/HomePage.ts
import { BasePage } from './BasePage';

export class HomePage extends BasePage {
  readonly heroSection: Locator;
  readonly servicesSection: Locator;
  readonly productsSection: Locator;

  constructor(page: Page) {
    super(page);
    this.heroSection = page.locator('[data-testid="hero-section"]');
    this.servicesSection = page.locator('#services');
    this.productsSection = page.locator('#products');
  }

  async goto() {
    await this.navigate('/');
    await this.waitForPageLoad();
  }

  async scrollToSection(sectionId: string) {
    await this.page.locator(`#${sectionId}`).scrollIntoViewIfNeeded();
  }
}

使用示例

// e2e/src/tests/smoke/home-page.smoke.spec.ts
import { test, expect } from '@playwright/test';
import { HomePage } from '../../pages/HomePage';

test.describe('首页冒烟测试', () => {
  let homePage: HomePage;

  test.beforeEach(async ({ page }) => {
    homePage = new HomePage(page);
    await homePage.goto();
  });

  test('首页加载成功', async () => {
    await expect(homePage.heroSection).toBeVisible();
  });
});

测试配置

Playwright 配置

// e2e/playwright.config.ts
export default defineConfig({
  testDir: './src/tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 4 : '50%',
  reporter: [
    ['html'],
    ['json', { outputFile: 'test-results/results.json' }],
    ['junit', { outputFile: 'test-results/junit.xml' }],
    ['allure-playwright'],
  ],
  timeout: 90000,
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'on-first-retry',
    headless: true,
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { 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'] } },
  ],
  webServer: {
    command: 'cd .. && npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});

环境配置

// e2e/src/config/environments.ts
export interface Environment {
  name: string;
  baseURL: string;
  retries: number;
  trace: 'on' | 'off' | 'on-first-retry';
  screenshot: 'on' | 'off' | 'only-on-failure';
  video: 'on' | 'off' | 'on-first-retry';
  headless: boolean;
  slowMo: number;
}

export const environments: Record<string, Environment> = {
  development: {
    name: 'development',
    baseURL: 'http://localhost:3000',
    retries: 0,
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'on-first-retry',
    headless: true,
    slowMo: 0,
  },
  production: {
    name: 'production',
    baseURL: 'https://www.novalon.cn',
    retries: 2,
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'on-first-retry',
    headless: true,
    slowMo: 0,
  },
};

export function getEnvironment(): Environment {
  const env = process.env.TEST_ENV || 'development';
  return environments[env];
}

运行测试

本地运行

# 进入测试目录
cd e2e

# 安装依赖
npm install

# 安装浏览器
npx playwright install

# 运行所有测试
npm run test

# 运行特定测试
npx playwright test path/to/test.spec.ts

# 运行带标签的测试
npx playwright test --grep @smoke

# UI 模式
npm run test:ui

# 调试模式
npm run test:debug

# 有头模式
npm run test:headed

CI 环境运行

# CI 模式(禁止 only、增加重试)
CI=true npx playwright test

测试报告

HTML 报告

npx playwright show-report

Allure 报告

# 生成报告
npm run test:allure

# 打开报告
npm run test:allure:open

# 实时服务
npm run test:allure:serve

JUnit 报告

用于 CI 集成,输出到 test-results/junit.xml

测试最佳实践

1. 使用数据测试 ID

// 组件中
<div data-testid="hero-section">

// 测试中
await page.locator('[data-testid="hero-section"]')

2. 等待策略

// 等待元素可见
await expect(locator).toBeVisible();

// 等待网络空闲
await page.waitForLoadState('networkidle');

// 等待特定响应
await page.waitForResponse('**/api/contact');

3. 避免硬编码等待

// 不推荐
await page.waitForTimeout(1000);

// 推荐
await expect(locator).toBeVisible();

4. 使用 Fixtures

// e2e/src/fixtures/base.fixture.ts
import { test as base } from '@playwright/test';
import { HomePage } from '../pages/HomePage';

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

5. 测试隔离

test.describe('测试组', () => {
  test.beforeEach(async ({ page }) => {
    // 每个测试前的初始化
  });

  test.afterEach(async ({ page }) => {
    // 每个测试后的清理
  });
});

CI 集成

Woodpecker CI 配置

# .woodpecker.yml
pipeline:
  e2e-tests:
    image: node:18-alpine
    environment:
      NODE_ENV: test
      CI: true
    commands:
      - cd e2e
      - npm ci
      - npx playwright install --with-deps chromium
      - npm run test:ci
    when:
      event:
        - push
        - pull_request

测试命令

{
  "scripts": {
    "test": "playwright test",
    "test:smoke": "playwright test --grep @smoke",
    "test:regression": "playwright test --grep @regression",
    "test:performance": "playwright test --grep @performance",
    "test:responsive": "playwright test --grep @responsive",
    "test:visual": "playwright test --grep @visual",
    "test:accessibility": "playwright test --grep @accessibility",
    "test:security": "playwright test --grep @security",
    "test:ci": "playwright test --reporter=html,json,junit"
  }
}

调试技巧

1. Trace Viewer

# 运行测试并生成 trace
npx playwright test --trace on

# 查看 trace
npx playwright show-trace trace.zip

2. 截图和视频

// 手动截图
await page.screenshot({ path: 'debug.png' });

// 元素截图
await locator.screenshot({ path: 'element.png' });

// 全页截图
await page.screenshot({ path: 'full.png', fullPage: true });

3. 控制台日志

// 监听控制台
page.on('console', msg => console.log(msg.text()));

// 监听页面错误
page.on('pageerror', error => console.error(error));

4. Playwright Inspector

npx playwright test --debug