如何用PHP项目实现Mock服务?

wen java案例 2

本文目录导读:

如何用PHP项目实现Mock服务?

  1. 使用PHPUnit进行单元测试Mock
  2. 使用Mockery库
  3. 使用Prophecy库
  4. 创建独立的Mock服务器
  5. 使用配置驱动的Mock框架
  6. 使用现有Mock工具
  7. 最佳实践建议

我来详细介绍在PHP项目中实现Mock服务的几种常见方法:

使用PHPUnit进行单元测试Mock

安装PHPUnit

composer require --dev phpunit/phpunit

使用PHPUnit内置Mock功能

<?php
use PHPUnit\Framework\TestCase;
class UserServiceTest extends TestCase
{
    public function testGetUser()
    {
        // 创建Mock对象
        $mockRepository = $this->createMock(UserRepositoryInterface::class);
        // 配置Mock行为
        $mockRepository->method('findById')
            ->with(1)
            ->willReturn(new User(['id' => 1, 'name' => 'John']));
        // 注入Mock对象
        $userService = new UserService($mockRepository);
        // 验证结果
        $result = $userService->getUser(1);
        $this->assertEquals('John', $result->getName());
    }
}

使用Mockery库

安装Mockery

composer require --dev mockery/mockery

使用示例

<?php
use Mockery\Adapter\Phpunit\MockeryTestCase;
class OrderServiceTest extends MockeryTestCase
{
    public function testProcessOrder()
    {
        // 创建Mock对象
        $mockPaymentGateway = Mockery::mock(PaymentGatewayInterface::class);
        // 设置预期行为
        $mockPaymentGateway->shouldReceive('charge')
            ->once()
            ->with(500, 'USD')
            ->andReturn(true);
        // 创建Mock对象并验证方法调用顺序
        $mockEmailService = Mockery::mock(EmailService::class);
        $mockEmailService->shouldReceive('sendOrderConfirmation')
            ->once()
            ->andReturn(true);
        // 注入Mock对象
        $orderService = new OrderService($mockPaymentGateway, $mockEmailService);
        $result = $orderService->processOrder(new Order(['amount' => 500]));
        $this->assertTrue($result);
    }
}

使用Prophecy库

安装Prophecy

composer require --dev phpspec/prophecy

使用示例

<?php
use Prophecy\PhpUnit\ProphecyTrait;
use PHPUnit\Framework\TestCase;
class DatabaseServiceTest extends TestCase
{
    use ProphecyTrait;
    public function testSaveRecord()
    {
        // 创建预言对象
        $database = $this->prophesize(DatabaseInterface::class);
        // 设置预期行为
        $database->save(['name' => 'test'])
            ->shouldBeCalled()
            ->willReturn(true);
        $database->getLastInsertId()
            ->willReturn(123);
        // 获取Mock对象
        $databaseMock = $database->reveal();
        $service = new DataService($databaseMock);
        $result = $service->saveData(['name' => 'test']);
        $this->assertEquals(123, $result);
    }
}

创建独立的Mock服务器

使用Slim框架实现Mock API服务器

<?php
// mock_server.php
require 'vendor/autoload.php';
use Slim\Factory\AppFactory;
$app = AppFactory::create();
// Mock API端点
$app->get('/api/users/{id}', function ($request, $response, $args) {
    $users = [
        1 => ['id' => 1, 'name' => 'John', 'email' => 'john@example.com'],
        2 => ['id' => 2, 'name' => 'Jane', 'email' => 'jane@example.com'],
    ];
    $userId = $args['id'];
    if (isset($users[$userId])) {
        $payload = json_encode($users[$userId]);
        $response->getBody()->write($payload);
        return $response->withHeader('Content-Type', 'application/json');
    }
    $response->getBody()->write(json_encode(['error' => 'User not found']));
    return $response->withStatus(404)->withHeader('Content-Type', 'application/json');
});
// 带延迟的Mock
$app->get('/api/slow-endpoint', function ($request, $response) {
    sleep(3); // 模拟延迟
    $payload = json_encode(['status' => 'completed']);
    $response->getBody()->write($payload);
    return $response->withHeader('Content-Type', 'application/json');
});
// 条件响应Mock
$app->post('/api/orders', function ($request, $response) {
    $data = json_decode($request->getBody(), true);
    if (empty($data['product_id'])) {
        $response->getBody()->write(json_encode(['error' => 'Product ID required']));
        return $response->withStatus(400);
    }
    $payload = json_encode([
        'order_id' => rand(1000, 9999),
        'status' => 'created',
        'product_id' => $data['product_id']
    ]);
    $response->getBody()->write($payload);
    return $response->withStatus(201)->withHeader('Content-Type', 'application/json');
});
// 运行Mock服务器
$app->run();

使用配置驱动的Mock框架

创建通用的Mock服务类

<?php
class MockService
{
    private $config;
    private $responses;
    public function __construct()
    {
        $this->loadConfig();
    }
    private function loadConfig()
    {
        $this->config = [
            'endpoints' => [
                'GET /api/users' => [
                    'response' => [
                        ['id' => 1, 'name' => 'John'],
                        ['id' => 2, 'name' => 'Jane']
                    ],
                    'status' => 200,
                    'delay' => 0
                ],
                'GET /api/users/{id}' => [
                    'response' => function($params) {
                        return ['id' => $params['id'], 'name' => 'User ' . $params['id']];
                    },
                    'status' => 200
                ],
                'POST /api/login' => [
                    'response' => [
                        'token' => 'mock_token_' . uniqid(),
                        'expires_in' => 3600
                    ],
                    'status' => 200,
                    'validate' => function($body) {
                        return !empty($body['username']) && !empty($body['password']);
                    }
                ]
            ]
        ];
    }
    public function handleRequest($method, $path, $body = null)
    {
        $key = "$method $path";
        if (!isset($this->config['endpoints'][$key])) {
            return $this->notFoundResponse();
        }
        $endpoint = $this->config['endpoints'][$key];
        // 验证请求
        if (isset($endpoint['validate'])) {
            if (!$endpoint['validate']($body)) {
                return $this->validationErrorResponse();
            }
        }
        // 处理延迟
        if (isset($endpoint['delay']) && $endpoint['delay'] > 0) {
            sleep($endpoint['delay']);
        }
        // 生成响应
        $response = $endpoint['response'];
        if (is_callable($response)) {
            $response = $response([]); // 传递参数
        }
        return [
            'status' => $endpoint['status'],
            'body' => $response,
            'headers' => ['Content-Type' => 'application/json']
        ];
    }
    private function notFoundResponse()
    {
        return [
            'status' => 404,
            'body' => ['error' => 'Endpoint not found'],
            'headers' => ['Content-Type' => 'application/json']
        ];
    }
    private function validationErrorResponse()
    {
        return [
            'status' => 400,
            'body' => ['error' => 'Validation failed'],
            'headers' => ['Content-Type' => 'application/json']
        ];
    }
}

使用现有Mock工具

php-mock(函数级别Mock)

composer require --dev php-mock/php-mock-phpunit
<?php
use phpmock\phpunit\PHPMock;
class ExternalServiceTest extends TestCase
{
    use PHPMock;
    public function testExternalCall()
    {
        // Mock内置函数
        $mock = $this->getFunctionMock('App\\Service', 'file_get_contents');
        $mock->expects($this->once())
            ->willReturn('{"status": "ok"}');
        $service = new ExternalService();
        $result = $service->callExternal();
        $this->assertEquals('ok', $result['status']);
    }
}

最佳实践建议

组织Mock文件结构

tests/
├── Mocks/
│   ├── UserRepositoryMock.php
│   ├── PaymentGatewayMock.php
│   └── EmailServiceMock.php
├── Unit/
│   └── Service/
└── Integration/

Mock工厂模式

<?php
class MockFactory
{
    public static function createUserRepository(array $users = [])
    {
        $mock = Mockery::mock(UserRepositoryInterface::class);
        $mock->shouldReceive('findById')
            ->andReturnUsing(function ($id) use ($users) {
                return $users[$id] ?? null;
            });
        $mock->shouldReceive('save')
            ->andReturn(true);
        return $mock;
    }
}
// 使用
$userRepo = MockFactory::createUserRepository([
    1 => new User(['id' => 1, 'name' => 'Test'])
]);

选择合适的Mock方法取决于你的需求:

  • 单元测试:使用PHPUnit内置Mock或Mockery
  • 集成测试:使用Prophecy或创建独立的Mock服务器
  • API开发:使用Slim等框架创建Mock API服务器
  • 复杂场景:使用配置驱动的自定义Mock服务

建议组合使用这些方法,根据不同的测试需求选择最合适的方案。

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