本文目录导读:

我来详细介绍在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服务
建议组合使用这些方法,根据不同的测试需求选择最合适的方案。