Mockery is an expressive, simple, and flexible PHP mock object framework for use with PHPUnit, PHPSpec, and other testing frameworks. It allows developers to create mock objects for testing purposes, enabling isolation of the code under test from its dependencies. This is crucial for unit testing, where you want to test a specific unit of code without worrying about the external systems or complex dependencies it relies on. By using mocks, you can simulate the behavior of these dependencies, control their responses, and verify that your code interacts with them as expected.
Key concepts in Mockery:
1. Mock Objects: Objects that stand in for real dependencies during testing. They record interactions and allow expectations to be set.
2. Stubs: Mocks that are primarily used to provide predefined answers to method calls during a test, without necessarily asserting on those calls.
3. Spies: Real objects that are wrapped by Mockery. You can call methods on the real object, and then later verify interactions with the spy.
4. Expectations: Define what methods are expected to be called on a mock, with what arguments, how many times, and what they should return. Common methods include `shouldReceive()`, `andReturn()`, `once()`, `twice()`, `times()`, `never()`, `with()`, `byDefault()`, `andThrow()`, etc.
Why use Mockery?
* Isolation: Decouple the unit under test from its external dependencies, making tests faster and more reliable.
* Control: Precisely control the behavior of dependencies, simulating various scenarios (e.g., successful calls, errors, specific data returns).
* Verification: Assert that the unit under test interacts correctly with its dependencies (e.g., calls specific methods with specific arguments).
* Test-Driven Development (TDD): Facilitates TDD by allowing you to define the interfaces of dependencies before they are fully implemented.
* Expressive Syntax: Mockery provides a fluent and readable API for defining mock object behavior.
Installation is typically done via Composer:
`composer require mockery/mockery --dev`
In a test class, you usually create mocks in your `setUp()` method or directly within a test, set expectations, execute the code under test, and then verify the results. It's vital to call `Mockery::close()` in your `tearDown()` method to clear any mock expectations and prevent state leakage between tests.
Example Code
<?php
// Imagine we have a User repository interface and its implementation
interface UserRepositoryInterface
{
public function findById(int $id): ?array;
public function save(array $userData): bool;
}
// And a UserService that uses this repository
class UserService
{
private UserRepositoryInterface $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
public function getUserProfile(int $userId): ?array
{
$user = $this->userRepository->findById($userId);
if ($user) {
// Simulate some business logic, e.g., adding a 'full_name'
$user['full_name'] = ($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '');
}
return $user;
}
public function createUser(array $data): bool
{
if (!isset($data['email']) || !isset($data['password'])) {
return false;
}
// More validation can go here...
return $this->userRepository->save($data);
}
}
// Now, let's write a PHPUnit test for UserService using Mockery
require 'vendor/autoload.php'; // Composer autoloader
use PHPUnit\Framework\TestCase;
use Mockery\MockInterface;
class UserServiceTest extends TestCase
{
/ @var MockInterface|UserRepositoryInterface */
protected $userRepositoryMock;
protected UserService $userService;
protected function setUp(): void
{
parent::setUp();
// Create a mock for the UserRepositoryInterface
$this->userRepositoryMock = Mockery::mock(UserRepositoryInterface::class);
// Instantiate the UserService with the mocked dependency
$this->userService = new UserService($this->userRepositoryMock);
}
protected function tearDown(): void
{
// Close Mockery to clean up expectations after each test
Mockery::close();
parent::tearDown();
}
public function testGetUserProfileReturnsUserWhenFound(): void
{
$userId = 1;
$mockUserData = [
'id' => $userId,
'first_name' => 'John',
'last_name' => 'Doe',
'email' => 'john.doe@example.com'
];
// Set an expectation on the mock:
// - userRepositoryMock should receive the 'findById' method.
// - It should be called exactly once.
// - It should be called with argument '1'.
// - When called, it should return $mockUserData.
$this->userRepositoryMock->shouldReceive('findById')
->once()
->with($userId)
->andReturn($mockUserData);
// Call the method under test
$result = $this->userService->getUserProfile($userId);
// Assert the expected outcome
$this->assertIsArray($result);
$this->assertEquals($userId, $result['id']);
$this->assertEquals('John Doe', $result['full_name']);
$this->assertEquals($mockUserData['email'], $result['email']);
}
public function testGetUserProfileReturnsNullWhenNotFound(): void
{
$userId = 99;
// Set an expectation: findById is called with 99 and returns null
$this->userRepositoryMock->shouldReceive('findById')
->once()
->with($userId)
->andReturn(null);
// Call the method under test
$result = $this->userService->getUserProfile($userId);
// Assert the expected outcome
$this->assertNull($result);
}
public function testCreateUserSuccessfully(): void
{
$userData = [
'email' => 'test@example.com',
'password' => 'hashed_password',
'first_name' => 'Test',
'last_name' => 'User'
];
// Set an expectation: save is called once with $userData and returns true
$this->userRepositoryMock->shouldReceive('save')
->once()
->with($userData)
->andReturn(true);
// Call the method under test
$result = $this->userService->createUser($userData);
// Assert the expected outcome
$this->assertTrue($result);
}
public function testCreateUserFailsWithoutRequiredData(): void
{
$userData = ['first_name' => 'Invalid']; // Missing email and password
// Set an expectation: The 'save' method should never be called
$this->userRepositoryMock->shouldNotReceive('save');
// Call the method under test
$result = $this->userService->createUser($userData);
// Assert the expected outcome
$this->assertFalse($result);
}
}
// To run this test:
// 1. Make sure you have Composer installed.
// 2. Create a composer.json file with:
// {
// "require-dev": {
// "phpunit/phpunit": "^9.5",
// "mockery/mockery": "^1.5"
// },
// "autoload": {
// "psr-4": {
// "": "."
// }
// }
// }
// 3. Run `composer install`.
// 4. Save the code above as `UserServiceTest.php` in the same directory.
// 5. Run `./vendor/bin/phpunit UserServiceTest.php` from your terminal.








Mockery/Mockery