You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对PHP REST API做单元测试?含Web/MySQL服务Mock及Travis CI适配

Mocking Web/MySQL Servers & Travis CI Setup for PHPUnit Tests

Hey there! Great that you already have a grasp on writing PHPUnit tests—let's tackle your remaining questions with practical, actionable solutions.

Mocking Web Servers in PHPUnit

If your code makes HTTP requests (e.g., with Guzzle, cURL, or native functions), you don't need to hit a real web server for tests. Here are two reliable approaches:

1. Use Guzzle's Mock Handler (for Guzzle users)

Guzzle has a built-in mock system to simulate HTTP responses without network calls. Example:

use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

// Define mock responses (match what your code expects)
$mockHandler = new MockHandler([
    new Response(200, ['Content-Type' => 'application/json'], json_encode(['status' => 'ok', 'data' => 'test'])),
    // Add more responses for sequential requests if needed
]);

// Create a client using the mock handler
$handlerStack = HandlerStack::create($mockHandler);
$client = new Client(['handler' => $handlerStack]);

// Now any request made with $client will return your mock response
$response = $client->get('https://api.example.com/data');
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('test', json_decode($response->getBody())->data);

2. Mock HTTP Client Interfaces (for custom/native requests)

If you're using custom HTTP logic or native functions like file_get_contents, wrap that logic in an interface and mock it with PHPUnit or Mockery. Example with Mockery:

use Mockery;
use PHPUnit\Framework\TestCase;

// First, define an interface for your HTTP client
interface HttpClientInterface {
    public function get(string $url): string;
}

// Your actual implementation uses file_get_contents
class NativeHttpClient implements HttpClientInterface {
    public function get(string $url): string {
        return file_get_contents($url);
    }
}

// In your test, mock the interface
class MyServiceTest extends TestCase {
    public function testFetchExternalData() {
        $mockHttpClient = Mockery::mock(HttpClientInterface::class);
        $mockHttpClient->shouldReceive('get')
            ->with('https://api.example.com/data')
            ->once()
            ->andReturn(json_encode(['data' => 'mocked']));

        // Inject the mock into your service
        $service = new MyService($mockHttpClient);
        $result = $service->fetchData();

        $this->assertEquals('mocked', $result['data']);
    }

    protected function tearDown(): void {
        Mockery::close();
    }
}

Mocking MySQL Servers & Testing Database Code

You have two paths here: mocking the database client (for unit tests) or using a real test database (for integration tests).

1. Mock Database Clients (Unit Tests)

If you want to isolate your code from the actual database, mock PDO, MySQLi, or your database repository interfaces. Example mocking PDO with Mockery:

use Mockery;
use PHPUnit\Framework\TestCase;

class UserRepositoryTest extends TestCase {
    public function testGetUserById() {
        // Mock the PDOStatement first
        $mockStmt = Mockery::mock(\PDOStatement::class);
        $mockStmt->shouldReceive('execute')
            ->with([123])
            ->once();
        $mockStmt->shouldReceive('fetch')
            ->andReturn(['id' => 123, 'name' => 'Geri']);

        // Mock the PDO instance
        $mockPdo = Mockery::mock(\PDO::class);
        $mockPdo->shouldReceive('prepare')
            ->with('SELECT * FROM users WHERE id = ?')
            ->once()
            ->andReturn($mockStmt);

        // Inject mock PDO into your repository
        $repo = new UserRepository($mockPdo);
        $user = $repo->getById(123);

        $this->assertEquals('Geri', $user['name']);
    }

    protected function tearDown(): void {
        Mockery::close();
    }
}

2. Real Test Database (Integration Tests)

For testing actual SQL queries and database interactions, use a dedicated test database (never your production DB!). Here's how to set it up:

  • Use SQLite In-Memory: Fast and zero setup for simple tests. Example with PDO:
    $pdo = new PDO('sqlite::memory:');
    // Run schema migrations for your test tables
    $pdo->exec('CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(255))');
    
  • Transaction Rollbacks: Wrap each test in a database transaction and roll it back after the test to keep data clean:
    protected function setUp(): void {
        parent::setUp();
        $this->pdo->beginTransaction();
    }
    
    protected function tearDown(): void {
        $this->pdo->rollBack();
        parent::tearDown();
    }
    
  • PHPUnit Database Extension: Automatically manages test data (inserts/rollbacks) for you, supporting MySQL, PostgreSQL, and more.

Setting Up Travis CI for MySQL Tests

Travis CI has built-in support for MySQL—here's a sample .travis.yml configuration to get you started:

language: php

# Test against multiple PHP versions (adjust as needed)
php:
  - 7.4
  - 8.0
  - 8.1
  - 8.2

# Configure MySQL version (8.0 is current stable)
addons:
  mysql: 8.0

before_script:
  # Create your test database (Travis uses root with empty password by default)
  - mysql -u root -e "CREATE DATABASE IF NOT EXISTS my_test_db;"
  # Run your database migrations (adjust command to match your setup)
  - php bin/console doctrine:migrations:migrate --no-interaction # For Symfony
  # OR
  - php migrate.php # For custom migration scripts
  # Set environment variables for your test database
  - export DB_HOST=localhost
  - export DB_USER=root
  - export DB_PASSWORD=""
  - export DB_DATABASE=my_test_db

# Run your PHPUnit tests
script:
  - phpunit --configuration phpunit.xml.dist

Key Notes for Travis CI:

  • Travis CI's MySQL instance has root user with no password by default.
  • For faster tests, consider using SQLite in-memory instead of MySQL (no need for the addons section then).
  • Most common MySQL extensions (like pdo_mysql) are pre-installed for supported PHP versions.

内容的提问来源于stack exchange,提问作者Geri Borbás

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:29:17