本文目录导读:

我来详细解释PHP如何进行模拟(Mock)操作:
使用 PHPUnit 框架进行模拟
安装 PHPUnit
composer require --dev phpunit/phpunit
基本模拟示例
<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
class UserServiceTest extends TestCase
{
public function testGetUser()
{
// 创建模拟对象
$mockRepository = $this->createMock(UserRepository::class);
// 设置预期行为
$mockRepository->method('findById')
->with(1)
->willReturn(new User('John Doe'));
// 注入模拟对象
$service = new UserService($mockRepository);
$user = $service->getUser(1);
$this->assertEquals('John Doe', $user->getName());
}
}
不同类型的模拟
Stub(桩) - 返回固定值
$stub = $this->createMock(SomeClass::class);
$stub->method('calculate')
->willReturn(42);
Mock(模拟) - 验证行为
$mock = $this->createMock(SomeClass::class);
$mock->expects($this->once())
->method('doSomething')
->with($this->equalTo('test'))
->willReturn('result');
高级模拟技巧
模拟接口
interface PaymentGateway {
public function process($amount);
public function refund($transactionId);
}
class PaymentTest extends TestCase
{
public function testPayment()
{
$gateway = $this->createMock(PaymentGateway::class);
$gateway->expects($this->once())
->method('process')
->with(100)
->willReturn(true);
$payment = new PaymentProcessor($gateway);
$result = $payment->pay(100);
$this->assertTrue($result);
}
}
模拟复杂对象
class ComplexMockTest extends TestCase
{
public function testComplexMock()
{
// 使用 MockBuilder 进行更复杂的设置
$mock = $this->getMockBuilder(UserService::class)
->disableOriginalConstructor()
->setMethods(['getUserById', 'saveUser'])
->getMock();
$mock->method('getUserById')
->willReturn(new User(1, 'John'));
$mock->expects($this->once())
->method('saveUser')
->with($this->callback(function($user) {
return $user->getName() === 'John';
}));
}
}
使用 Prophecy 库(推荐)
安装 Prophecy
composer require --dev phpspec/prophecy
示例代码
use Prophecy\PhpUnit\ProphecyTrait;
class ProphecyTest extends TestCase
{
use ProphecyTrait;
public function testWithProphecy()
{
$prophecy = $this->prophesize(UserRepository::class);
// 设置预期
$prophecy->findById(1)
->willReturn(new User('Jane'));
$prophecy->save()
->shouldBeCalled();
// 获取模拟对象
$repository = $prophecy->reveal();
$service = new UserService($repository);
$user = $service->getUser(1);
$this->assertEquals('Jane', $user->getName());
}
}
模拟静态方法
使用 AspectMock(需要额外安装)
composer require --dev codeception/aspect-mock
class StaticMethodTest extends TestCase
{
public function testStaticMethod()
{
$mock = $this->createMock(Logger::class);
// 模拟静态方法
$mock->staticExpects($this->once())
->method('log')
->with('test message');
}
}
模拟 HTTP 请求
使用 Guzzle Mock Handler
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Psr7\Response;
class HttpClientTest extends TestCase
{
public function testMockHttpClient()
{
$mock = new MockHandler([
new Response(200, ['Content-Type' => 'application/json'], json_encode(['success' => true])),
]);
$client = new Client(['handler' => $mock]);
$response = $client->get('/api/data');
$this->assertEquals(200, $response->getStatusCode());
}
}
最佳实践
完整测试示例
<?php
namespace App\Tests;
use PHPUnit\Framework\TestCase;
use App\Services\OrderService;
use App\Repositories\OrderRepository;
use App\Models\Order;
use App\Services\PaymentService;
class OrderServiceTest extends TestCase
{
private $orderRepository;
private $paymentService;
private $orderService;
protected function setUp(): void
{
parent::setUp();
// 创建模拟对象
$this->orderRepository = $this->createMock(OrderRepository::class);
$this->paymentService = $this->createMock(PaymentService::class);
// 注入依赖
$this->orderService = new OrderService(
$this->orderRepository,
$this->paymentService
);
}
public function testProcessOrderSuccessfully()
{
$orderData = [
'id' => 1,
'total' => 100,
'status' => 'pending'
];
// 设置模拟行为
$this->orderRepository->method('findById')
->with(1)
->willReturn(new Order($orderData));
$this->paymentService->expects($this->once())
->method('processPayment')
->with(100)
->willReturn(true);
$this->orderRepository->expects($this->once())
->method('updateStatus')
->with(1, 'completed');
// 执行测试
$result = $this->orderService->processOrder(1);
// 断言
$this->assertTrue($result);
}
public function testProcessOrderFailsDueToPayment()
{
$this->orderRepository->method('findById')
->willReturn(new Order(['id' => 1, 'total' => 100]));
$this->paymentService->method('processPayment')
->willReturn(false);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Payment failed');
$this->orderService->processOrder(1);
}
}
PHP模拟的常用方法:
- PHPUnit Mock - 最常用,内置支持
- Prophecy - 更友好的API,推荐用于复杂场景
- MockBuilder - 用于复杂对象模拟
- Guzzle Mock - 专门用于HTTP请求模拟
选择建议:
- 简单场景:使用 PHPUnit 内置模拟
- 复杂行为验证:使用 Prophecy
- 需要模拟 HTTP:使用 Guzzle Mock Handler
这样可以有效隔离测试,提高代码质量和可维护性。