PHP项目上线前如何做自动化测试

wen PHP项目 30

本文目录导读:

PHP项目上线前如何做自动化测试

  1. 分层测试策略(从左到右,成本递增,反馈递减)
  2. 具体工具选择与配置
  3. CI/CD 集成(以 GitHub Actions 为例)
  4. 关键里程碑与最佳实践
  5. 针对不同框架的快速启动命令
  6. 常见问题与解决方案
  7. 上线前必须通过的最低质量门禁

为 PHP 项目上线前搭建自动化测试体系,核心目标是在代码合并前部署前快速发现回归问题、逻辑错误和性能瓶颈,以下是分步骤、分层次的实施指南,涵盖环境、工具链和具体操作。

分层测试策略(从左到右,成本递增,反馈递减)

层级 测试类型 主要工具 执行频率 关键作用
第一层 静态分析 & 代码规范 PHPStan, Psalm, PHP_CodeSniffer 每次提交 避免低级错误和风格不一致
第二层 单元测试 PHPUnit, Pest 每次提交 验证函数/类逻辑正确性
第三层 集成测试 PHPUnit + Testcontainer 每次提交/每日 验证数据库、缓存、API交互
第四层 端到端测试 Playwright, Cypress, Selenium 每日/预发布 模拟用户真实浏览器操作
第五层 性能/压力测试 k6, Apache JMeter, Blackfire 预发布前 验证响应时间、并发能力

具体工具选择与配置

静态分析(防止部署后出现 TypeError、undefined 方法)

  • PHPStan:推荐 level max(目前是 9 或 10),或者至少 level 6
  • 配置示例 (phpstan.neon):
    parameters:
        level: 9
        paths:
            - src/
            - tests/
        excludePaths:
            - vendor/
  • 运行命令vendor/bin/phpstan analyse

单元测试与集成测试

  • PHPUnit:行业标准。

  • 关键配置 (phpunit.xml):

    <!-- 开启代码覆盖率,但 CI 中可关闭以加速 -->
    <phpunit bootstrap="vendor/autoload.php" colors="true"
             cacheResult="false" processIsolation="false">
        <testsuites>
            <testsuite name="Unit">
                <directory>tests/Unit</directory>
            </testsuite>
            <testsuite name="Feature">
                <directory>tests/Feature</directory>
            </testsuite>
        </testsuites>
        <coverage>
            <include>
                <directory suffix=".php">app/</directory>
            </include>
        </coverage>
    </phpunit>
  • 数据库测试:使用 RefreshDatabase trait 或 Testcontainer(推荐后者,速度更快且不依赖本地环境)。

    // 使用 Laravel 的 RefreshDatabase 示例
    use Illuminate\Foundation\Testing\RefreshDatabase;
    class UserTest extends TestCase
    {
        use RefreshDatabase;
        public function test_can_create_user()
        {
            $user = User::factory()->create();
            $this->assertDatabaseHas('users', ['email' => $user->email]);
        }
    }
  • Mock 外部依赖:用 Mockery 或 PHPUnit 内置的 createMock 模拟 HTTP 请求、邮件服务等。

端到端测试(E2E)

  • 工具Playwright(推荐,现代、跨浏览器、录制回放方便)。

  • 示例 (使用 PHP 调用 Node 运行,或直接 Node 脚本):

    // tests/e2e/login.spec.js
    const { test, expect } = require('@playwright/test');
    test('用户应该能成功登录', async ({ page }) => {
        await page.goto('https://staging.example.com/login');
        await page.fill('input[name="email"]', 'user@example.com');
        await page.fill('input[name="password"]', 'password');
        await page.click('button[type="submit"]');
        // 断言页面跳转到仪表盘
        await expect(page).toHaveURL(/.*dashboard/);
        await expect(page.locator('h1')).toContainText('欢迎回来');
    });

性能测试

  • k6 (轻量级,JavaScript 编写脚本):

    import http from 'k6/http';
    import { check } from 'k6';
    export const options = {
        vus: 10,          // 10 个虚拟用户
        duration: '30s',  // 持续 30 秒
        thresholds: {
            http_req_duration: ['p(95)<200'],  // 95% 请求响应时间 < 200ms
            http_req_failed: ['rate<0.01'],    // 错误率 < 1%
        },
    };
    export default function () {
        let res = http.get('https://example.com/health');
        check(res, { 'status is 200': (r) => r.status === 200 });
    }

CI/CD 集成(以 GitHub Actions 为例)

创建一个 .github/workflows/ci.yml 文件,将上述工具串联起来:

name: PHP CI Pipeline
on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
jobs:
  quality:
    runs-on: ubuntu-latest
    services:
      # 启动 MySQL/PostgreSQL 用于集成测试
      mysql:
        image: mysql:8.0
        env:
          MYSQL_DATABASE: testing
          MYSQL_ROOT_PASSWORD: root
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=3
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mbstring, pdo_mysql, bcmath
          tools: composer, phpstan, php-cs-fixer
      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist --optimize-autoloader
      # 1. 静态分析
      - name: Run PHPStan
        run: vendor/bin/phpstan analyse --memory-limit=256M --no-progress
      - name: Check coding style
        run: vendor/bin/php-cs-fixer fix --dry-run --diff
      # 2. 单元测试 + 集成测试 (需要数据库)
      - name: Run PHPUnit tests
        env:
          DB_CONNECTION: mysql
          DB_HOST: 127.0.0.1
          DB_PORT: 3306
          DB_DATABASE: testing
          DB_USERNAME: root
          DB_PASSWORD: root
        run: vendor/bin/phpunit --log-junit test-report.xml --coverage-text
      # 3. 构建并部署到测试环境(如果上述测试通过)
      - name: Deploy to Staging
        if: github.ref == 'refs/heads/develop' && success()
        run: |
          # 调用部署脚本,例如使用 Deployer 或 rsync
          echo "Deploying to staging..."
      # 4. 运行 E2E 测试(在 Staging 上)
      - name: Run E2E tests
        if: github.ref == 'refs/heads/develop' && success()
        run: |
          npx playwright test --config=tests/e2e/playwright.config.js
      # 5. 性能测试(预发布阶段手动触发)
      # - name: Load Test (Manual trigger)
      #   if: github.event_name == 'workflow_dispatch'
      #   run: k6 run tests/performance/health.js

关键里程碑与最佳实践

  1. 从“单元测试”开始:不需要一开始就追求 E2E 全覆盖,先保证核心逻辑(支付、订单、用户认证)有单元测试覆盖。
  2. 测试覆盖率目标
    • 上线初期:核心功能 60%+ 覆盖。
    • 稳定期:逻辑层 80%+ 覆盖。
    • 注意:覆盖率数字是参考,更要关注测试质量(是否测试了异常路径、边界值)。
  3. 数据隔离
    • 单元测试:使用 Mock,完全不碰数据库。
    • 集成测试:使用 Testcontainer 创建临时数据库,测试完自动销毁。
    • 禁止在测试中使用线上或生产数据库。
  4. 测试夹具(Fixtures)管理
    • 使用 Factory 模式(如 Laravel Factory 或 fakerphp/faker)生成测试数据,避免硬编码。
    • 不要在测试之间共享数据,每个测试独立设置和清理(setUp / tearDown)。
  5. 失败即阻断
    • CI 配置为:任意测试失败 → 阻止合并 PR 或阻止部署
    • 设置测试超时时间(PHPUnit 设置 timeoutForSmallTests="5"),避免死循环挂起整个 CI。
  6. 可视化报告
    • 使用 phpunit --testdox 生成人类可读的测试描述。
    • clover.xmljunit.xml 上传到 SonarQube 或 Codecov,让团队能看到覆盖率趋势。

针对不同框架的快速启动命令

框架 测试命令 配置文件
Laravel php artisan test (包含 PHPUnit + Dusk 浏览器测试) phpunit.xml
Symfony php bin/phpunit phpunit.xml.dist
Yii2 vendor/bin/codecept run codeception.yml
纯 PHP vendor/bin/phpunit phpunit.xml

常见问题与解决方案

问题 表现 解决方案
测试太慢 跑一次全量测试需要 30 分钟+ 拆分测试套件:Unit 每次提交跑,FeatureE2E 按需或只在 PR 合并前跑。 2. 使用 --parallel--jobs 并行运行。 3. 缓存 Composer 依赖。
数据库测试不稳定 偶发失败,或与环境相关 严格使用事务回滚(DatabaseTransactions trait)。 2. 升级到 Testcontainer 避免本地数据库状态污染。 3. 避免测试依赖具体 ID 值。
Mock 过多 测试只测了 happy path,缺乏真实感 边界条件异常写集成测试(走真实 I/O)。 2. 使用契约测试(Contract Testing)验证 API 响应结构。
CI 中无浏览器 E2E 无法运行 使用容器化运行 Playwright(microsoft/playwright:latest 镜像)。

上线前必须通过的最低质量门禁

  1. PHPStan Level 9 无错误(最大发现率,最小误报)。
  2. 单元测试全部通过(至少覆盖核心服务层和模型逻辑)。
  3. 关键集成测试通过(数据库增删改查、外部 API 调用(Mock))。
  4. 代码规范通过php-cs-fixerphpcs)。
  5. (可选但强烈推荐) 安全扫描:运行 composer audit 检查依赖漏洞。

自动化测试不是一蹴而就的,但一旦建立起来,它会成为项目最大的“刹车”——帮你把问题拦截在上线之前,而不是等到用户报告。

抱歉,评论功能暂时关闭!