PHP - Mocking and Stubbing in PHP Tests
Mocking and stubbing are essential techniques used in unit testing to isolate the code being tested. They allow developers to simulate the behavior of dependencies so that tests focus only on the logic of the unit under test, without relying on external systems such as databases, APIs, or file systems.
Understanding the Need for Mocking and Stubbing
In real-world applications, classes often depend on other classes or services. These dependencies may:
-
Perform slow operations (database queries, API calls)
-
Produce unpredictable results
-
Be difficult to set up in a test environment
To ensure reliable and fast tests, these dependencies are replaced with controlled test doubles such as mocks and stubs.
What is Stubbing
A stub is a simplified implementation of a dependency that returns predefined data. It does not contain real logic and is used only to provide consistent inputs to the test.
Example of Stubbing
class UserRepositoryStub {
public function getUserById($id) {
return ['id' => $id, 'name' => 'Test User'];
}
}
In this case, instead of querying a real database, the stub returns fixed data. This ensures that the test behaves consistently every time.
Purpose of Stubs
Stubs are used when:
-
You need predictable input data
-
The real dependency is slow or unavailable
-
You want to isolate the unit being tested
They do not verify how they are used; they only provide data.
What is Mocking
A mock is a more advanced test double that not only simulates behavior but also verifies interactions. It checks whether certain methods were called, how many times they were called, and with what arguments.
Example of Mocking Concept
Instead of just returning data, a mock ensures that a method is invoked correctly:
-
Was a specific method called?
-
How many times was it called?
-
Were the correct parameters passed?
Example Using PHPUnit Mock
use PHPUnit\Framework\TestCase;
class UserServiceTest extends TestCase {
public function testSendWelcomeEmail() {
$mailer = $this->createMock(Mailer::class);
$mailer->expects($this->once())
->method('send')
->with($this->equalTo('[email protected]'));
$service = new UserService($mailer);
$service->sendWelcomeEmail('[email protected]');
}
}
Here, the mock verifies that the send method is called exactly once with the correct email.
Key Differences Between Mocking and Stubbing
-
Stubs provide predefined data without verifying interactions
-
Mocks both simulate behavior and verify method calls
-
Stubs focus on inputs, mocks focus on behavior
Types of Test Doubles
Mocking and stubbing fall under a broader category called test doubles. These include:
-
Dummy: Placeholder object with no behavior
-
Stub: Returns fixed data
-
Mock: Verifies interactions
-
Spy: Records information about method calls
-
Fake: Has a simplified but functional implementation
Benefits of Mocking and Stubbing
These techniques improve testing in several ways:
-
Enable isolation of the unit under test
-
Make tests faster by avoiding real dependencies
-
Provide predictable and repeatable results
-
Allow testing of edge cases and error conditions
-
Improve confidence in code correctness
Best Practices
-
Use stubs when you only need to control returned data
-
Use mocks when you need to verify behavior or interactions
-
Avoid overusing mocks, as it can make tests fragile
-
Keep tests simple and focused on one behavior
-
Mock external systems, not internal logic
Common Tools in PHP
Several tools support mocking and stubbing:
-
PHPUnit (built-in mocking framework)
-
Mockery (a popular mocking library)
-
Prophecy (used in older PHPUnit versions)
Challenges
-
Over-mocking can lead to tests that depend too much on implementation details
-
Poorly designed mocks can make tests hard to maintain
-
Misuse can reduce test reliability instead of improving it
Conclusion
Mocking and stubbing are powerful techniques in PHP testing that help isolate code and ensure reliable unit tests. Stubs provide controlled data, while mocks verify interactions between components. When used correctly, they make testing faster, more predictable, and more effective, contributing to higher-quality software and better maintainability.