PHP项目集成测试如何搭建自动化流程

wen PHP项目 25

本文目录导读:

PHP项目集成测试如何搭建自动化流程

  1. 整体架构设计
  2. 基础设施搭建
  3. CI/CD配置示例
  4. 测试框架配置
  5. 集成测试示例
  6. 测试脚本与自动化
  7. 监控与报告
  8. 最佳实践

我来详细介绍PHP项目集成测试自动化流程的搭建方案:

整体架构设计

graph LR
    A[代码提交] --> B[代码检查]
    B --> C[单元测试]
    C --> D[集成测试]
    D --> E[代码质量]
    E --> F[部署]

基础设施搭建

测试环境容器化

# Dockerfile.test
FROM php:8.2-fpm
# 安装依赖
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libpq-dev \
    libmysqlclient-dev \
    && docker-php-ext-install pdo pdo_mysql pdo_pgsql
# 安装Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# 安装测试工具
RUN pecl install xdebug && docker-php-ext-enable xdebug
WORKDIR /var/www/html

Docker Compose测试环境

# docker-compose.test.yml
version: '3.8'
services:
  test-runner:
    build:
      context: .
      dockerfile: Dockerfile.test
    environment:
      - APP_ENV=test
      - DB_HOST=test-db
      - DB_DATABASE=test_db
      - DB_USERNAME=test_user
      - DB_PASSWORD=test_pass
      - REDIS_HOST=test-redis
    volumes:
      - .:/var/www/html
    depends_on:
      - test-db
      - test-redis
  test-db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: test_db
      MYSQL_USER: test_user
      MYSQL_PASSWORD: test_pass
      MYSQL_ROOT_PASSWORD: root_pass
    tmpfs: /var/lib/mysql
  test-redis:
    image: redis:alpine

CI/CD配置示例

GitHub Actions配置

# .github/workflows/integration-tests.yml
name: Integration Tests
on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_DATABASE: test_db
          MYSQL_USER: test_user
          MYSQL_PASSWORD: test_pass
          MYSQL_ROOT_PASSWORD: root_pass
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s
      redis:
        image: redis:alpine
        ports:
          - 6379:6379
    steps:
    - uses: actions/checkout@v3
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        extensions: mbstring, pdo_mysql, bcmath, xdebug
        coverage: xdebug
    - name: Install Dependencies
      run: |
        composer install --no-interaction --prefer-dist
        cp .env.testing .env
    - name: Setup Database
      run: |
        php artisan migrate --env=testing
        php artisan db:seed --env=testing
    - name: Run Integration Tests
      run: |
        php artisan test --filter=Integration
        # 或使用PHPUnit
        vendor/bin/phpunit --testsuite=Integration
    - name: Generate Coverage Report
      run: |
        vendor/bin/phpunit --coverage-clover=coverage.xml
    - name: Upload Coverage
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage.xml

测试框架配置

PHPUnit配置

<!-- phpunit.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
>
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
        <testsuite name="Integration">
            <directory suffix="Test.php">./tests/Integration</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <coverage processUncoveredFiles="true">
        <include>
            <directory suffix=".php">./app</directory>
        </include>
        <report>
            <clover outputFile="coverage.xml"/>
            <html outputDirectory="coverage-html"/>
        </report>
    </coverage>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="DB_CONNECTION" value="mysql"/>
        <env name="DB_HOST" value="127.0.0.1"/>
        <env name="DB_PORT" value="3306"/>
        <env name="DB_DATABASE" value="test_db"/>
        <env name="DB_USERNAME" value="test_user"/>
        <env name="DB_PASSWORD" value="test_pass"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_CONNECTION" value="sync"/>
        <env name="MAIL_MAILER" value="array"/>
    </php>
</phpunit>

集成测试示例

API集成测试

<?php
// tests/Integration/Api/UserApiTest.php
namespace Tests\Integration\Api;
use Tests\TestCase;
use App\Models\User;
use App\Models\Role;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserApiTest extends TestCase
{
    use RefreshDatabase;
    protected function setUp(): void
    {
        parent::setUp();
        // 创建测试数据
        $this->user = User::factory()->create([
            'email' => 'test@example.com',
            'password' => bcrypt('password123')
        ]);
        $this->actingAs($this->user);
    }
    /** @test */
    public function it_can_create_user()
    {
        $response = $this->postJson('/api/users', [
            'name' => 'New User',
            'email' => 'new@example.com',
            'password' => 'password123',
            'role_id' => Role::factory()->create()->id
        ]);
        $response->assertStatus(201)
                 ->assertJsonStructure([
                     'data' => [
                         'id',
                         'name',
                         'email',
                         'created_at'
                     ]
                 ]);
        $this->assertDatabaseHas('users', [
            'email' => 'new@example.com'
        ]);
    }
    /** @test */
    public function it_can_update_user()
    {
        $response = $this->putJson("/api/users/{$this->user->id}", [
            'name' => 'Updated Name'
        ]);
        $response->assertStatus(200);
        $this->assertEquals('Updated Name', $this->user->fresh()->name);
    }
}

数据库集成测试

<?php
// tests/Integration/Database/UserRepositoryTest.php
namespace Tests\Integration\Database;
use Tests\TestCase;
use App\Models\User;
use App\Repositories\UserRepository;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserRepositoryTest extends TestCase
{
    use RefreshDatabase;
    private UserRepository $repository;
    protected function setUp(): void
    {
        parent::setUp();
        $this->repository = app(UserRepository::class);
    }
    /** @test */
    public function it_can_find_active_users()
    {
        // 创建测试数据
        User::factory()->count(5)->create(['status' => 'active']);
        User::factory()->count(3)->create(['status' => 'inactive']);
        $activeUsers = $this->repository->findActiveUsers();
        $this->assertCount(5, $activeUsers);
        $this->assertTrue($activeUsers->every(fn($user) => $user->status === 'active'));
    }
    /** @test */
    public function it_can_get_users_by_role()
    {
        $role = Role::factory()->create(['name' => 'admin']);
        User::factory()->count(3)->create(['role_id' => $role->id]);
        $adminUsers = $this->repository->getUsersByRole('admin');
        $this->assertCount(3, $adminUsers);
    }
}

外部服务集成测试

<?php
// tests/Integration/Services/PaymentServiceTest.php
namespace Tests\Integration\Services;
use Tests\TestCase;
use App\Services\PaymentService;
use App\Models\Order;
use Mockery;
class PaymentServiceTest extends TestCase
{
    public function test_successful_payment_processing()
    {
        // 创建测试订单
        $order = Order::factory()->create([
            'amount' => 100.00,
            'status' => 'pending'
        ]);
        // 模拟外部支付网关
        $gatewayMock = Mockery::mock('App\Contracts\PaymentGateway');
        $gatewayMock->shouldReceive('charge')
                    ->once()
                    ->with($order->amount, $order->id)
                    ->andReturn([
                        'success' => true,
                        'transaction_id' => 'txn_12345'
                    ]);
        $service = new PaymentService($gatewayMock);
        $result = $service->processPayment($order);
        // 验证结果
        $this->assertTrue($result['success']);
        $this->assertEquals('txn_12345', $result['transaction_id']);
        // 验证订单状态变更
        $this->assertEquals('paid', $order->fresh()->status);
    }
    public function test_failed_payment_handling()
    {
        // 测试支付失败的处理逻辑
    }
}

测试脚本与自动化

Makefile自动化脚本

# Makefile
.PHONY: test integration-test coverage lint
test:
    docker-compose -f docker-compose.test.yml run --rm test-runner vendor/bin/phpunit
integration-test:
    docker-compose -f docker-compose.test.yml run --rm test-runner vendor/bin/phpunit --testsuite=Integration
coverage:
    docker-compose -f docker-compose.test.yml run --rm test-runner \
        vendor/bin/phpunit --coverage-html=coverage-html
lint:
    vendor/bin/phpcs --standard=PSR12 app/ tests/
test-all: lint test coverage

数据库迁移与种子

<?php
// tests/CreatesApplication.php
namespace Tests;
use Illuminate\Contracts\Console\Kernel;
trait CreatesApplication
{
    public function createApplication()
    {
        $app = require __DIR__.'/../bootstrap/app.php';
        $app->make(Kernel::class)->bootstrap();
        return $app;
    }
}
<?php
// database/seeders/DatabaseSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call([
            RoleSeeder::class,
            PermissionSeeder::class,
            UserSeeder::class,
            ProductSeeder::class,
        ]);
    }
}

监控与报告

测试报告输出

<?php
// tests/Reports/TestReportGenerator.php
namespace Tests\Reports;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class TestReportGenerator
{
    public function generate(array $testResults): void
    {
        $report = [
            'timestamp' => now()->toDateTimeString(),
            'total_tests' => count($testResults),
            'passed' => 0,
            'failed' => 0,
            'skipped' => 0,
            'duration' => 0,
            'details' => []
        ];
        foreach ($testResults as $result) {
            $report[$result['status']]++;
            $report['details'][] = $result;
        }
        Storage::disk('local')->put(
            "test-reports/report-{$report['timestamp']}.json",
            json_encode($report, JSON_PRETTY_PRINT)
        );
        Log::info('Test Report Generated', $report);
    }
}

监控集成

<?php
// config/test-monitoring.php
return [
    'slack' => [
        'webhook_url' => env('SLACK_WEBHOOK_URL'),
        'channel' => '#test-reports'
    ],
    'metrics' => [
        'enabled' => true,
        'statsd_host' => env('STATSD_HOST', 'localhost'),
        'statsd_port' => 8125
    ]
];

最佳实践

测试环境隔离

// .env.testing
APP_ENV=testing
APP_DEBUG=true
DB_CONNECTION=mysql
DB_HOST=test-db
DB_DATABASE=test_db
DB_USERNAME=test_user
DB_PASSWORD=test_pass
CACHE_DRIVER=array
SESSION_DRIVER=array
MAIL_MAILER=array
QUEUE_CONNECTION=sync

并行测试配置

<!-- phpunit.xml -->
<phpunit>
    <testsuites>
        <testsuite name="Integration">
            <directory suffix="Test.php">./tests/Integration</directory>
        </testsuite>
    </testsuites>
    <extensions>
        <extension class="Symfony\Component\Panther\ServerExtension"/>
    </extensions>
    <!-- 并行测试配置 -->
    <php>
        <ini name="memory_limit" value="512M"/>
        <env name="PARALLEL_TESTING" value="true"/>
    </php>
</phpunit>

测试数据管理

<?php
// tests/Traits/TestDataManager.php
namespace Tests\Traits;
trait TestDataManager
{
    protected function createTestData(array $config = []): array
    {
        return [
            'users' => User::factory()
                        ->count($config['users_count'] ?? 10)
                        ->create(),
            'products' => Product::factory()
                         ->count($config['products_count'] ?? 20)
                         ->create(),
            'orders' => Order::factory()
                        ->count($config['orders_count'] ?? 30)
                        ->create()
        ];
    }
    protected function cleanupTestData(): void
    {
        DB::statement('SET FOREIGN_KEY_CHECKS=0');
        foreach ($this->getTestTables() as $table) {
            DB::table($table)->truncate();
        }
        DB::statement('SET FOREIGN_KEY_CHECKS=1');
    }
    protected function getTestTables(): array
    {
        return ['users', 'products', 'orders', 'payments'];
    }
}

这套方案涵盖了PHP项目集成测试自动化的完整流程,从环境搭建到测试执行,再到报告生成和监控,关键是要根据自己的项目需求进行调整,并保持测试环境的稳定性和一致性。

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