top of page
Writer's pictureThe Tech Platform

Mocking in PHP for Unit Testing: A Complete Guide

Ever wondered how programmers make sure their code works correctly in PHP? Well, they use a cool trick called 'mocking'. Mocking allows you to simulate the behavior of various components, such as classes, interfaces, and functions, during the testing phase.


In this article, you will explore the fundamentals of Mocking in PHP. You will also learn the essential concepts, how to create mock objects, and discover the benefits and practical use cases of mocking.


Let's begin!


Table of Contents:

Why is Mocking useful for Unit Testing?

Benefits of Mocking

When to use Mocking

Code Example

Mocking Classes

Mocking Interface

Mocking Functions


What is Mocking in PHP?

Mocking is a technique used in software development specifically in unit testing. It involves creating simulated or "mock" objects to mimic the behavior of real objects or components that a piece of code depends on. Mock objects simulate the responses and interactions of these real components without actually invoking their functionality. Mocking is typically used when you want to isolate the code being tested and focus on its behavior in isolation from the external dependencies.


Why is Mocking useful for Unit Testing?

Mocking is useful for several reasons in the context of unit testing:

  1. Isolation: Unit tests aim to test a specific unit or component of code in isolation from the rest of the system. By using mocks, you can isolate the code under test from external dependencies, ensuring that the test focuses solely on the unit's behavior.

  2. Control: Mock objects allow you to control and manipulate the behavior of external dependencies. This enables you to simulate various scenarios and edge cases, making it easier to test different conditions and error paths.

  3. Speed: Real external dependencies, such as databases, APIs, or network services, can be slow or unreliable. Using mocks eliminates the need to make actual calls to these dependencies, making your unit tests faster and more predictable.

  4. Consistency: Mocks provide consistent and deterministic behavior, which is crucial for reliable unit testing. Real-world external dependencies may change or have varying states, making it challenging to write consistent tests.

  5. Reduced Complexity: Testing with real external dependencies can introduce complexity and configuration overhead. Mocking simplifies the testing process by allowing you to create controlled and predictable test environments.

Benefits of using Mocking

  1. Faster Tests: Mocks execute quickly because they don't involve real-world interactions that can be time-consuming.

  2. Isolation: Code is tested in isolation, ensuring that failures are due to issues in the unit being tested rather than external factors.

  3. Reproducibility: Mocks provide consistent results, allowing you to reproduce test cases reliably.

  4. Easier Debugging: When tests fail, it's easier to identify the source of the problem since you're only dealing with a specific unit of code.

  5. Parallel Testing: Mocked dependencies can be easily parallelized, improving test suite execution time.

When to use Mocking

Mocking is particularly useful in the following scenarios:

  1. External Dependencies: When your code interacts with external resources like databases, APIs, or file systems, you should use mocking to avoid the overhead of real calls.

  2. Unstable or Slow Resources: For unstable or slow access resources, using mocks ensures your tests run quickly and reliably.

  3. Controlled Testing: When you need to test specific scenarios, error conditions, or edge cases that are difficult to trigger with real dependencies.

  4. Unit Testing: In unit testing, you want to test individual units of code in isolation, and mocking helps achieve this isolation.


Code Example Using Mocking (Python with unittest.mock):

Let's say you have a simple class that fetches data from an external API and processes it. Here's how you might use mocking for unit testing:

# data_processor.py

import requests

class DataProcessor:
    def __init__(self, api_url):
        self.api_url = api_url

    def fetch_and_process_data(self):
        response = requests.get(self.api_url)
        if response.status_code == 200:
            data = response.json()
            # Process the data (e.g., calculate a sum)
            result = sum(data)
            return result
        else:
            return None

Now, let's write a unit test for the DataProcessor class using mocking:

# test_data_processor.py

import unittest
from unittest.mock import patch
from data_processor import DataProcessor

class TestDataProcessor(unittest.TestCase):
    @patch('data_processor.requests.get')
    def test_fetch_and_process_data(self, mock_get):
        # Create a mock response object
        mock_response = unittest.mock.Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = [1, 2, 3, 4, 5]

        # Configure the mock to return the mock response
        mock_get.return_value = mock_response

        # Create an instance of DataProcessor
        processor = DataProcessor(api_url='http://example.com/data')

        # Test the method
        result = processor.fetch_and_process_data()

        # Assertions
        self.assertEqual(result, 15)  # The sum of [1, 2, 3, 4, 5]
        
if __name__ == '__main__':
    unittest.main()

In this example, we use the @patch decorator to mock the requests.get function. This allows us to control its behavior and return a mock response. We then create an instance of DataProcessor, call its fetch_and_process_data method, and assert that it correctly processes the data returned by the mock API call.


Getting started with Mocking in PHP

Consider the below diagram that shows the process of using a mocking framework to provide fake object testing. The mocking framework is a tool that allows developers to create fake objects that they can use to test their code. This makes it easier to test the code in a controlled environment and to verify that it is working as expected.

Mocking in PHP for Unit Testing

The image shows the following steps:

  1. The actual code makes an external call, such as a call to a database.

  2. The unit test replaces the external call with a call to a mock object.

  3. The mock object simulates the behavior of the real external object.

  4. The unit test verifies that the mock object was called as expected.

This process allows the developer to test the code in isolation from its dependencies. This makes the tests more reliable and easier to maintain.


Below is a step-by-step guide to help you get started with mocking in PHP:


1. Choosing a Mocking Framework:

Choosing a mocking framework is an essential first step. PHPUnit is a popular choice for PHP unit testing, and it provides built-in support for creating and using mock objects.


Below is the list of another popular mocking framework for PHP unit testing:

  1. Mockery - Standalone mocking framework for PHP. Provide simple and expressive syntax. Flexible and easy to use.

  2. Prophecy - Mocking library included in the PHP testing framework (PHPSpec). Clean and intuitive syntax to create mock objects. You can also use it independently.

  3. Phake - Simple and lightweight. Best for developers who are new to mocking. Also, offers basic mocking capabilities without extensive features.

  4. AspectMock - It uses the Aspect-Object Programming (AOP) principle. It allows you to mock classes and methods at runtime. It has the ability to mock classes that cannot be easily mocked using traditional means.

To get started:


Install PHPUnit if you haven't already by running:

composer require --dev phpunit/phpunit 

2. Creating Mock Objects:

Mock objects are essential for simulating the behavior of real objects or classes. In PHPUnit, you can create mock objects using the createMock method. Here's how you can create mock objects:


Create a mock object for a class:

$mock = $this->createMock(MyClass::class); 

Create a mock object for an interface:

$mock = $this->createMock(MyInterface::class); 

Create a mock object with custom methods (partial mock):

$mock = $this->getMockBuilder(MyClass::class)     
    ->setMethods(['method1', 'method2'])     
    ->getMock(); 

3. Defining the Behavior of Mock Objects:

Once you've created a mock object, you can define its behavior for testing. This includes specifying what methods should return and what exceptions to throw. Here's how to define the behavior of mock objects:


Define the behavior of a method to return a value:

$mock->method('someMethod')->willReturn('mocked result'); 

Define the behavior of a method to throw an exception:

$mock->method('anotherMethod')->willThrowException(new \Exception('Mocked exception')); 

Use the mock object in your test to verify its behavior:

$result = $mock->someMethod(); 
$this->assertEquals('mocked result', $result);  

$this->expectException(\Exception::class); 
$mock->anotherMethod(); 

4. Verifying Method Calls (Optional):

In addition to defining behavior, you can also verify how many times a method is called and with which arguments. This is helpful for ensuring that your code under test interacts correctly with the mock objects. Here's an example of verifying method calls:

// Expect 'someMethod' to be called exactly once
$mock->expects($this->once())
    ->method('someMethod')
    ->with('argument1', 'argument2');

5. Running Your Tests:

Finally, you can run your tests using PHPUnit. Make sure to include your test class with the test methods. You can run PHPUnit from the command line:

./vendor/bin/phpunit YourTestFile.php

This command will execute your tests and provide feedback on whether they pass or fail.


By following these steps, you can effectively start using mocking in PHP using PHPUnit. Mocking is a valuable technique for writing reliable and isolated unit tests, helping you ensure that your code behaves correctly while isolating it from external dependencies.


Mocking Dependencies in PHP

Mocking dependencies in PHP refers to the practice of replacing real external components, such as classes, interfaces, functions, or objects, with mock objects during unit testing. The purpose of mocking dependencies is to isolate the code under test, ensuring that it is evaluated in isolation from its actual external dependencies. This isolation allows you to control the behavior of dependencies, making it easier to test different scenarios and ensure that the code functions correctly.


Mocking Classes

Mocking classes in PHP is a crucial aspect of unit testing, as it allows you to isolate the code you want to test from its external dependencies. When you mock a class, you replace the real class with a mock object that simulates the behavior of the class without executing its actual methods. This enables you to control and verify the interactions with the class under test. PHPUnit is a popular testing framework for PHP that provides built-in support for mocking classes, and it's widely used for this purpose.


Here's a step-by-step guide on how to mock classes in PHP using PHPUnit:


STEP 1. Import Necessary Classes:

First, you need to import the necessary classes from PHPUnit. Make sure your test class extends PHPUnit\Framework\TestCase to utilize PHPUnit's mocking capabilities:

use PHPUnit\Framework\TestCase;

STEP 2. Create a Mock Object:

To mock a class in PHPUnit, you use the createMock method provided by the TestCase class. You specify the class you want to mock as a parameter to createMock. The method then returns a mock object that can be used to replace the original class. Here's an example:

$mock = $this->createMock(MyClass::class);

In this example, MyClass is the class you want to mock. $mock is now an instance of the mock object that simulates the behavior of MyClass.


STEP 3. Define the Behavior of Methods:

Once you have the mock object, you can define the behavior of its methods using the method method on the mock object. You specify the name of the method you want to mock and use PHPUnit's fluent interface to specify how it should behave. For example, you can set a return value for a method like this:

$mock->method('someMethod')->willReturn('mocked result');

In this case, when you call $mock->someMethod(), it will return 'mocked result'.


STEP 4. Use the Mock Object in Your Test:

With the mock object prepared and the behavior of its methods defined, you can now use it in your test. You would typically call methods on the mock object within your test methods to simulate interactions with the mocked class.

$result = $mock->someMethod();
$this->assertEquals('mocked result', $result);

In this example, $result will contain the value 'mocked result' as defined in the mock's behavior.


STEP 5. Assertions and Verification:

After using the mock object in your test, you can also use PHPUnit's assertion methods to verify that specific methods were called or to assert the expected behavior of the code under test.

$mock->expects($this->once())->method('someMethod');

This line asserts that the someMethod of the mock object should be called exactly once during the test.


Mocking classes in PHP, especially with PHPUnit, allows you to create controlled and isolated test environments. You replace real classes with mock objects and define how those mock objects should behave, enabling you to thoroughly test the code that interacts with these classes while keeping the external dependencies under control. This practice is essential for effective unit testing and ensuring the reliability of your PHP code.


Mocking Interfaces

Mocking interfaces in PHP is a fundamental part of unit testing, just like mocking classes. When you mock an interface, you create a mock object that simulates the behavior of the interface's methods without executing their actual implementations. This allows you to control and verify interactions with objects that implement the interface. PHPUnit, a popular PHP testing framework, provides built-in support for mocking interfaces.


Let's explore how to mock interfaces in PHP using PHPUnit:


STEP 1. Import Necessary Classes:

Import the necessary classes as well:

use PHPUnit\Framework\TestCase;

STEP 2. Create a Mock Object for the Interface:

To mock an interface in PHPUnit, you can use the createMock method provided by the TestCase class, just like when mocking classes. Pass the interface you want to mock as a parameter to createMock. This method returns a mock object that simulates the behavior defined in the interface. Here's an example:

$mock = $this->createMock(MyInterface::class);

In this example, MyInterface is the interface you want to mock, and $mock is an instance of the mock object that imitates the behavior specified in MyInterface.


STEP 3. Define the Behavior of Interface Methods:

Specify the name of the method you want to mock and use PHPUnit's fluent interface to specify how it should behave. For example, you can set a return value for a method:

$mock->method('someMethod')->willReturn('mocked result');

In this case, when you call $mock->someMethod(), it will return 'mocked result'.


STEP 4. Use the Mock Object in Your Test:

Call methods on the mock object within your test methods to simulate interactions with objects that implement the interface.

$result = $mock->someMethod();
$this->assertEquals('mocked result', $result);

In this example, $result will contain the value 'mocked result' as specified in the mock's behavior.


STEP 5. Assertions and Verification:

As with mocking classes, after using the mock object in your test, you can use PHPUnit's assertion methods to verify that specific methods were called or to assert the expected behavior of the code under test.

$mock->expects($this->once())->method('someMethod');

This line asserts that the someMethod of the mock object should be called exactly once during the test.


This is a valuable technique for effective unit testing and ensuring that your code correctly interfaces with objects that adhere to certain contracts defined by the interface.


Mocking functions

Mocking functions, including global functions and static methods within a class, is essential for unit testing when you want to isolate the code under test from external dependencies or interactions. PHPUnit, a widely used PHP testing framework, provides a method called setMethods for this purpose. In this explanation, we'll cover how to mock functions using PHPUnit, including global functions and static methods.


STEP 1. Import Necessary Classes:

Import the necessary classes:

use PHPUnit\Framework\TestCase;

STEP 2. Create a Mock Object for the Class:

To mock a class that uses global functions or static methods, you can use the getMockBuilder method provided by the TestCase class. This method allows you to specify which methods should be mocked. Here's an example:

$mock = $this->getMockBuilder(MyClass::class)
    ->setMethods(['myGlobalFunction'])
    ->getMock();

In this example, you're creating a mock object for MyClass. The setMethods method specifies that you want to mock the myGlobalFunction method.


STEP 3. Define the Behavior of the Mocked Function:

Specify the name of the method you want to mock and use PHPUnit's fluent interface to specify how it should behave:

$mock->method('myGlobalFunction')->willReturn('mocked result');

STEP 4. Use the Mock Object in Your Test:

Call the methods of the mock object within your test methods to simulate interactions with the class.

$result = $mock->myGlobalFunction();
$this->assertEquals('mocked result', $result);

STEP 5. Assertions and Verification:

After using the mock object in your test, you can use PHPUnit's assertion methods to verify that the specific method was called or to assert the expected behavior of the code under test:

$mock->expects($this->once())->method('myGlobalFunction');

This line asserts that myGlobalFunction of the mock object should be called exactly once during the test.


This technique is valuable for testing code that relies on external functions without actually executing them, ensuring that your unit tests focus on the specific behavior of the code under test and not on external dependencies.


Advanced Mocking Techniques

Here are several advanced mocking techniques available for your use:

  1. Partial Mocks

  2. Spying

  3. Stubbing

1. Partial Mocks

Partial mocks are a mocking technique used in unit testing to create mock objects for a class or object, where only specific methods are mocked, while the real implementations of other methods remain intact. This allows you to isolate and control the behavior of certain methods in your unit tests while letting other methods execute their actual code.


Here's how you can use them with PHPUnit:

$mock = $this->getMockBuilder(MyClass::class)
    ->setMethods(['methodToMock'])
    ->getMock();

$mock->method('methodToMock')->willReturn('mocked result');

In this example, we create a partial mock of MyClass and specify that we want to mock only the methodToMock. The rest of the methods in MyClass will still execute their actual implementations.


When to Use:

Use partial mocks when you want to

  • Focus your unit test on a specific method or set of methods within a class.

  • Isolate the behavior of certain methods for testing without affecting other methods.

  • Test complex classes where you want to concentrate on specific functionality.

Advantages:

  • Allows you to isolate and control the behavior of specific methods.

  • Useful for testing complex classes where you want to concentrate on specific functionality.

  • The real implementations of unmocked methods remain intact, preserving their functionality.

Disadvantages:

  • Can become complex when dealing with many partially mocked methods.

  • Testing interactions with the real implementations can be challenging if not carefully managed.


2. Spying

Spying is a mocking technique used to monitor and verify that specific methods of an object are called during the execution of a unit test. Unlike traditional mocking, spying allows the real methods to be executed while still tracking method calls. It is useful for verifying interactions with methods without altering their behavior.


Here's how to create a spy with PHPUnit:

$spy = $this->getMockBuilder(MyClass::class)
    ->setMethods(['methodToSpyOn'])
    ->getMock();

$spy->expects($this->once())->method('methodToSpyOn');

// The methodToSpyOn will execute as usual, but you can verify its call later

In this example, we create a spy for MyClass and specify that we want to spy on the methodToSpyOn. The method is executed normally, but you can later use PHPUnit's expects method to verify that it was called as expected.


When to Use:

Use spying when you want to:

  • Monitor and verify that specific methods are called during the execution of a unit test.

  • Ensure that certain methods are invoked without affecting their actual behavior.

  • Verify interactions between objects and methods.

Advantages:

  • Allows you to monitor method calls without changing their behavior.

  • Useful for verifying interactions between objects and methods.

Disadvantages:

  • Doesn't modify the method's behavior, so you may still need stubbing or mocking for the method's return value or exceptions if required.


3. Stubbing

Stubbing is a mocking technique used to set predefined behavior for a method or function, such as specifying what value it should return. This allows you to control the method's outcomes in your unit tests without executing its actual implementation.


Here's how to create a stub with PHPUnit:

$stub = $this->getMockBuilder(MyClass::class)
    ->setMethods(['methodToStub'])
    ->getMock();

$stub->method('methodToStub')->willReturn('stubbed result');

// When you call methodToStub, it will return 'stubbed result' regardless of its actual implementation

In this example, we create a stub for MyClass and specify that we want to stub the methodToStub. When you call methodToStub, it will return 'stubbed result' as specified, regardless of its actual implementation.


When to Use:

Use stubbing when you want to:

  • Simulate specific conditions or responses from a method without executing its actual code.

  • Control the behavior of a method to handle different scenarios in your unit tests.

Advantages:

  • Allows you to define and control the behavior of a method.

  • Useful for testing different scenarios and handling specific return values or exceptions.

Disadvantages:

  • May not provide complete isolation if you stub methods that interact with external dependencies (e.g., databases or APIs).


Conclusion

Mocking in PHP is a powerful tool that helps ensure your code works as expected. It lets you test your code in isolation, making your applications more reliable. So, next time you're writing PHP code, don't forget to mock when needed, and happy coding!

1 Comment


bartmcleod
6 days ago

setMethods is deprecated...

Like
bottom of page