PHP集成测试怎么测接口

wen PHP项目 1

本文目录导读:

PHP集成测试怎么测接口

  1. 核心测试工具选择
  2. 测试数据管理策略
  3. 测试环境配置
  4. 性能优化技巧
  5. CI/CD集成配置
  6. 最佳实践建议

在PHP中进行接口集成测试,我有几种成熟的方案可以分享,让我从实际项目经验出发,给你一套完整的测试策略。

核心测试工具选择

PHPUnit + Guzzle(最常用)

<?php
use PHPUnit\Framework\TestCase;
use GuzzleHttp\Client;
class ApiIntegrationTest extends TestCase
{
    private $client;
    private $baseUri = 'http://api.example.com';
    protected function setUp(): void
    {
        $this->client = new Client([
            'base_uri' => $this->baseUri,
            'http_errors' => false,
            'timeout' => 5,
        ]);
    }
    /** @test */
    public function can_create_user()
    {
        // 准备测试数据
        $payload = [
            'name' => '张三',
            'email' => 'test@example.com',
            'phone' => '13800138000'
        ];
        // 发送请求
        $response = $this->client->post('/api/users', [
            'json' => $payload,
            'headers' => [
                'Authorization' => 'Bearer ' . $this->getAuthToken(),
                'X-Request-Id' => uniqid()
            ]
        ]);
        // 断言响应
        $this->assertEquals(201, $response->getStatusCode());
        $data = json_decode($response->getBody(), true);
        $this->assertArrayHasKey('user_id', $data);
        $this->assertEquals($payload['name'], $data['name']);
        // 验证数据已持久化
        $this->assertUserExists($data['user_id']);
    }
    /** @test */
    public function can_get_user_detail()
    {
        // 先创建测试用户
        $userId = $this->createTestUser();
        $response = $this->client->get('/api/users/' . $userId);
        $this->assertEquals(200, $response->getStatusCode());
        $user = json_decode($response->getBody(), true);
        $this->assertEquals($userId, $user['id']);
    }
    private function getAuthToken()
    {
        // 获取授权token的逻辑
        return 'your_test_token';
    }
    private function createTestUser()
    {
        // 创建测试用户并返回ID
        return 123;
    }
}

使用Codeception(更高级)

# acceptance.suite.yml
actor: ApiTester
modules:
    enabled:
        - REST:
            url: http://api.example.com/api
            depends: PhpBrowser
            part: Json
        - Db:
            dsn: 'mysql:host=localhost;dbname=test_db'
            user: 'root'
            password: ''
        - Asserts
<?php
// ApiCest.php
class ApiCest
{
    public function createUser(ApiTester $I)
    {
        $I->wantTo('创建新用户');
        // 设置请求数据
        $I->haveHttpHeader('Content-Type', 'application/json');
        $I->haveHttpHeader('Authorization', 'Bearer token123');
        // 发送POST请求
        $I->sendPost('/users', [
            'name' => '李四',
            'email' => 'lisi@example.com'
        ]);
        // 验证响应
        $I->seeResponseCodeIs(201);
        $I->seeResponseIsJson();
        $I->seeResponseContains('"status":"success"');
        $I->seeResponseMatchesJsonType([
            'user_id' => 'integer',
            'name' => 'string'
        ]);
        // 数据库验证
        $userId = $I->grabDataFromResponseByJsonPath('$.user_id')[0];
        $I->seeInDatabase('users', ['id' => $userId]);
    }
    public function getInvalidUser(ApiTester $I)
    {
        $I->wantTo('获取不存在的用户');
        $I->sendGet('/users/999999');
        $I->seeResponseCodeIs(404);
        $I->seeResponseContains('"error":"User not found"');
    }
}

使用Laravel/PHPUnit的测试特性

如果你用的是Laravel:

<?php
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class UserApiTest extends TestCase
{
    use RefreshDatabase; // 自动重置数据库
    /** @test */
    public function user_can_be_created()
    {
        $response = $this->withHeaders([
            'Accept' => 'application/json',
        ])->postJson('/api/users', [
            'name' => '王五',
            'email' => 'wangwu@example.com',
            'password' => 'secret123'
        ]);
        $response
            ->assertStatus(201)
            ->assertJson([
                'success' => true,
                'name' => '王五'
            ]);
        $this->assertDatabaseHas('users', [
            'name' => '王五',
            'email' => 'wangwu@example.com'
        ]);
    }
    /** @test */
    public function authentication_is_required()
    {
        // 未认证请求
        $response = $this->getJson('/api/users');
        $response->assertStatus(401);
        // 错误token
        $response = $this->getJson('/api/users', [
            'Authorization' => 'Bearer invalid-token'
        ]);
        $response->assertStatus(401);
    }
}

测试数据管理策略

使用Fixture/Factory模式

<?php
class TestDataFactory
{
    private $pdo;
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
    public function createUser($overrides = [])
    {
        $data = array_merge([
            'name' => '测试用户' . uniqid(),
            'email' => 'user' . uniqid() . '@example.com',
            'password' => password_hash('password123', PASSWORD_DEFAULT),
            'created_at' => date('Y-m-d H:i:s')
        ], $overrides);
        $stmt = $this->pdo->prepare(
            "INSERT INTO users (name, email, password, created_at) 
             VALUES (:name, :email, :password, :created_at)"
        );
        $stmt->execute($data);
        return $this->pdo->lastInsertId();
    }
    public function cleanup($userId)
    {
        $stmt = $this->pdo->prepare("DELETE FROM users WHERE id = ?");
        $stmt->execute([$userId]);
    }
}

测试环境配置

class BaseApiTest extends TestCase
{
    protected static $testDb;
    protected $client;
    public static function setUpBeforeClass(): void
    {
        // 创建测试数据库
        self::$testDb = new PDO(
            'mysql:host=localhost;dbname=api_test',
            'test_user',
            'test_pass'
        );
        // 运行数据库迁移
        self::runMigrations();
        // 加载基础测试数据
        self::loadFixtures();
    }
    protected function setUp(): void
    {
        // 启动事务
        self::$testDb->beginTransaction();
        // 创建HTTP客户端
        $this->client = new Client([
            'base_uri' => 'http://localhost:8080/api',
            'http_errors' => false,
            'defaults' => [
                'exceptions' => false,
                'allow_redirects' => true,
            ]
        ]);
    }
    protected function tearDown(): void
    {
        // 回滚事务,保持数据隔离
        self::$testDb->rollBack();
    }
}

性能优化技巧

<?php
class ApiTestSuite extends PHPUnit\Framework\TestCase
{
    private static $cache = [];
    public function testBatchEndpointPerformance()
    {
        $responses = [];
        // 并行请求
        $promises = [];
        for ($i = 0; $i < 10; $i++) {
            $promises[] = $this->client->getAsync('/api/items', [
                'query' => ['page' => $i + 1]
            ]);
        }
        $results = Promise\Utils::settle($promises)->wait();
        foreach ($results as $result) {
            $responses[] = $result['value'];
        }
        // 性能断言
        $startTime = microtime(true);
        // 执行关键操作
        $endTime = microtime(true);
        $executionTime = $endTime - $startTime;
        $this->assertLessThan(2.0, $executionTime, 'API响应时间超过2秒');
    }
    // 缓存token减少重复认证
    public function getSharedToken()
    {
        if (!isset(self::$cache['token'])) {
            self::$cache['token'] = $this->authenticate();
        }
        return self::$cache['token'];
    }
}

CI/CD集成配置

# .github/workflows/api-tests.yml
name: API Tests
on:
  push:
    paths:
      - 'src/**'
      - 'tests/**'
  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
        ports:
          - 3306:3306
    env:
      APP_ENV: testing
      DB_CONNECTION: mysql
      DB_DATABASE: test_db
      API_BASE_URL: http://localhost:8000
    steps:
      - uses: actions/checkout@v2
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mbstring, xml, pdo_mysql
      - name: Install Dependencies
        run: composer install
      - name: Start API Server
        run: |
          php artisan serve --port=8000 &
          sleep 3
      - name: Run Tests
        run: php artisan test --env=testing

最佳实践建议

测试隔离

class IsolatedTest extends TestCase
{
    protected function setUp(): void
    {
        // 每个测试独立的数据状态
        $this->truncateTestTables();
        $this->seedBaseData();
    }
}

错误场景覆盖

public function testValidationErrors()
{
    $response = $this->client->post('/api/users', [
        'json' => ['name' => ''] // 非法数据
    ]);
    $this->assertEquals(422, $response->getStatusCode());
    $errors = json_decode($response->getBody(), true);
    $this->assertArrayHasKey('name', $errors['errors']);
}

数据关系验证

public function testUserRelationship()
{
    // 创建用户和订单
    $userId = $this->createUser();
    $orderId = $this->createOrder($userId);
    // 获取用户详情时验证订单关系
    $response = $this->client->get("/api/users/{$userId}/orders");
    $orders = json_decode($response->getBody(), true);
    $this->assertCount(1, $orders);
    $this->assertEquals($orderId, $orders[0]['id']);
}

这套测试方案能帮你覆盖大部分接口测试场景,关键词是测试数据隔离环境一致性,确保在CI/CD中有稳定的测试环境,如果需要针对特定框架的详细配置,欢迎继续交流!

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