Mocking is a crucial technique in PHP testing that allows you to replace real dependencies with fake implementations、Here's a comprehensive guide to PHP mocking.
Popular Mocking Libraries
1、PHPUnit Mock Objects (Built-in)
php
use PHPUnit\Framework\TestCase;
class UserServiceTest extends TestCase
{
public function testGetUserReturnsUser()
{
// Create a mock of UserRepository
$userRepository = $this-createMock(UserRepository::class);
// Configure the mock
$userRepository-expects($this-once())
-method('findById')
-with($this-equalTo(1))
-willReturn(new User('John Doe'));
// Use the mock
$userService = new UserService($userRepository);
$user = $userService-getUser(1);
2、Mockery
php
use Mockery\Adapter\Phpunit\MockeryTestCase;
class OrderServiceTest extends MockeryTestCase
{
public function testProcessOrder()
{
// Create a mock
$paymentGateway = \Mockery::mock(PaymentGateway::class);
Example 1: Testing with Database Dependencies
php
interface UserRepositoryInterface
{
public function findById(int $id): ?User;
public function save(User $user): bool;
}
class UserService
{
private $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this-userRepository = $userRepository;
}
public function getUserProfile(int $userId): array
{
$user = $this-userRepository-findById($userId);
if (!$user) {
throw new UserNotFoundException(User not found);
}
public function testGetUserProfileThrowsExceptionWhenUserNotFound()
{
$userRepository = $this-createMock(UserRepositoryInterface::class);
$userRepository-method('findById')
-with(999)
-willReturn(null);
Example 2: Mocking External API Calls
php
interface HttpClientInterface
{
public function get(string $url): Response;
public function post(string $url, array $data): Response;
}
class WeatherService
{
private $httpClient;
private $apiKey;
public function __construct(HttpClientInterface $httpClient, string $apiKey)
{
$this-httpClient = $httpClient;
$this-apiKey = $apiKey;
}
public function getCurrentTemperature(string $city): float
{
$url = http://api.weather.com/v1/current?city={$city}&key={$this-apiKey};
$response = $this-httpClient-get($url);
// Test
class WeatherServiceTest extends TestCase
{
public function testGetCurrentTemperatureReturnsCorrectValue()
{
$response = $this-createMock(Response::class);
$response-method('getBody')
-willReturn('{temperature: 25.5, humidity: 60}');
$weatherService = new WeatherService($httpClient, 'test-key');
$temperature = $weatherService-getCurrentTemperature('London');
$this-assertEquals(25.5, $temperature);
}
}
Example 3: Partial Mocks
php
class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
public function multiply(int $a, int $b): int
{
return $a * $b;
}
public function calculateArea(int $length, int $width): int
{
// This method uses other methods we might want to mock them
return $this-multiply($length, $width);
}
}
// Test with partial mock
class CalculatorTest extends TestCase
{
public function testCalculateAreaUsesMultiply()
{
$calculator = $this-getMockBuilder(Calculator::class)
-onlyMethods(['multiply'])
-getMock();
Spies (Verify behavior after execution)
php
// Mockery spy example
public function testEmailIsSentAfterRegistration()
{
$mailer = \Mockery::spy(Mailer::class);
$userService = new UserService($mailer);
$userService-registerUser('john@65.hk', 'password');
// Verify after the fact
$mailer-shouldHaveReceived('sendWelcomeEmail')-once();
}
Stubbing Exceptions
php
// PHPUnit
$mock-method('dangerousMethod')
-willThrowException(new \Exception('Something went wrong'));
// Mockery
$mock-shouldReceive('dangerousMethod')
-andThrow(new \Exception('Something went wrong'));
Mocking Static Methods (with Mockery)
php
// For classes with static methods
\Mockery::mock('alias:Config')
-shouldReceive('get')
-with('database.host')
-andReturn('localhost');
Best Practices
1、Don't mock value objects Create real instances instead
2、Mock roles, not concrete classes Prefer interfaces
3、Keep mocks simple Complex mocks indicate design issues
4、Verify only what's necessary Don't over-specify expectations
5、Use descriptive test names Make it clear what you're testing
Installation
For Mockery:
bash
composer require dev mockery/mockery
PHPUnit is typically included with most PHP projects, but if needed:
bash
composer require dev phpunit/phpunit
This guide covers the essential aspects of PHP mocking、Choose the approach that best fits your testing framework and requirements.