An Introduction to Mock Object Testing

Share this article

If you are part of a development team, more often than not your code will also depend on code written by your teammates. But what if their code isn’t available at the moment — say, your teammate hasn’t finished writing it yet? Or, what if the code you need requires other external dependencies that are hard to set up? And what if you cannot test code because of other factors beyond your control? Will you just hang around, do nothing, and wait until your team is done or when everything is in place? Of course not! In this this article, I’ll illustrate how to write code that works around this problem with dependencies. Some background on unit testing is ideal, and there is already a great introductory article here on SitePoint on unit testing with written by Michelle Saver. And although it’s not required for this article, do check out my other articles on automated database testing.

The Case for Mock Objects

As you may already have guessed, mock objects are a way out of the sticky situations I mentioned in the introduction. But what are mock objects? Mock objects are objects that stand in for the real implementation of an actual object. Now why would you want a stand-in object instead of a real one? Mock objects are used in unit-testing to mimic the behavior of real objects in test cases. By using them the functionality of the object you are implementing would be easier to test. Here are some situations where the use of a mock object is beneficial:
  1. The real implementation of one or more dependencies of an object has not been realized yet. Let’s say you’re tasked to do some processing on some data from a database. You would probably call on a method of some form of data access object or data repository, but what if the database has not been setup yet? What if the there was no data available (a situation I’ve encountered one too many times) or if the code that queries the database hasn’t been written yet? A mock data access object simulates the real data access object by returning some pre-defined values. This frees you from the burden of having to setup the database, look for data or write the code that queries the database.
  2. The real implementation of the dependencies of an object depends on factors that are hard to simulate. Suppose you would want to tabulate the likes and comments of a Facebook post by day. Where will you get your data? Your Facebook developer account is new. Heck, you still do not have friends! How will you simulate likes and comments? Mock objects offer a better approach than bothering your fellow developers to like or comment on some posts for you. And how will you simulate all these actions in a range of days if you wanted to show data by day? How about by month? What do you do if the unthinkable happens and Facebook is down at the moment? A mock object can pretend to be the Facebook library and return the data you need. You don’t have to go through the hassles I just mentioned just to get started working on your task.

Mock Objects in Action

Now that we know what mock objects are, let’s see some examples in action. We’ll implement our simple functionality previously mentioned, e.g tabulating likes and comments of a Facebook post. We’ll start with the following unit test to define our expectations of how our object is going to be called and how what the return value will look like:
<?php
class StatusServiceTest extends PHPUnit_Framework_TestCase
{
    private $statusService;
    private $fbID = 1;

    public function setUp() {
        $this->statusService = new StatusService();
    }

    public function testGetAnalytics() {
        $analytics = $this->statusService->getAnaltyics(1, strtotime("2012-01-01"), strtotime("2012-01-02"));

        $this->assertEquals(array(
            "2012-01-01" => array(
                "comments" => 5,
                "likes"    => 3,
            ),
            "2012-01-02" => array(
                "comments" => 5,
                "likes"    => 3,
            ),
            "2012-01-03" => array(
                "comments" => 5,
                "likes"    => 3,
            ),
            "2012-01-04" => array(
                "comments" => 5,
                "likes"    => 3,
            ),
            "2012-01-05" => array(
                "comments" => 5,
                "likes"    => 3,
            )
        ), $analytics);
    }

}
If you run this test you will get a failure. This is expected, as we have not implemented anything yet! Now let’s write up our implementation of the service. Of course the first step is to get the data from Facebook, so let us to do that first:
<?php
class StatuService
{
    private $facebook;

    public function getAnalytics($id, $from, $to) {
        $post = $this->facebook->get($id);
    }
}
This test too will fail because the Facebook object is null. You could plug in an actual implementation creating an actual instance with a Facebook app ID etc., but what for? We already all know what agony is involved to do that when it let’s you deviate from the task at hand. We can spare ourselves by injecting a mock one instead! The way this is done using mock objects, at least in our case, is to create a class that has a get() method and returns mock values. This should fool our client into thinking that it is calling the real object implementation when in fact it was actually just mocked.
<?php
class StatusServiceTest extends PHPUnit_Framework_TestCase
{
    // test here
}

class MockFacebookLibrary
{
    public function get($id) {
        return array(
            // mock return from Facebook here
        );
    }
}
Now that we have a mock class, let’s instantiate an instance and then inject it into StatusService
so that it can be used. But first, update StatusService with a setter for the Facebook library:
<?php
class StatusService
{
    // other lines of code

    public function setFacebook($facebook) {
        $this->facebook = facebook;
    }
}
Now inject the mock Facebook library:
<?php
class StatusServiceTest extends PHPUnit_Framework_TestCase
{
    public function testGetAnalytics {
        $analytics = $this->statusService->getAnaltyics(1, strtotime("2012-01-01"), strtotime("2012-01-02"));
        $analytics->setFacebook(new MockFacebook());

        // code truncated
    }
}
The test still fails, but at least we don’t get the error related to calling a method on a non-object anymore. More importantly though, you have just addressed the need to fulfill this dependency. You can now start programming the business logic you are tasked to do and pass the test.
PHPUnit 3.6.7 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 3.00Mb
There was 1 failure:
1) StatusServiceTest::testGetAnalytics null does not match expected type "array".
/home/jeune/mock-object-tutorial/ManualMockStatusServiceTest.php:43
FAILURES! Tests: 1, Assertions: 1, Failures: 1.

Taking it a Step Further: Using a Mocking Framework

While certainly you can live with using hand-crafted mocks when you are just starting out, later on, as I found out myself, you might have the need to use a real mocking framework as your needs become more complicated. In this article, I’ll show how to use the mocking framework that comes with PHPUnit. In my experience, here are some benefits to using a mock framework over using manually written mocks:
  • You can afford to be lazy. I found this especially true if you are dealing with abstract classes with lots of abstract methods. You can afford to mock only certain methods of an abstract class or interface. If you were doing this manually, then you would have to implement all of them by hand. It saves you some typing and comes pre-packaged with type hinting as well; you only setup what you need and you don’t have to maintain a new class for each test case.
  • You can write cleaner code. Readability is the key here. Framework-based mocks make your tests easier to understand as your mocks are written in the test. You do not need scroll down or switch between files to view hand-written mocks written elsewhere. What if you needed to call your mock object more than once with different results? With framework-based mocks, the if-else boiler plate code required to do this is already well encapsulated. Hence, it is easier on the eyes.

Using PHPUnit’s Mocking Framework

Turning our attention to using PHPUnit’s Mocking Framework, the steps are actually pretty intuitive and, once you get the hang of it, it will be second nature. In this section we’ll be using PHPUnit’s Mocking Framework to create a mock object for our example situation. Before we do that however comment out or remove the line that uses our hand-crafted mock object in our test. We need to fail first so we have something to pass. Later on, we will inject our new mock implementation.
<?php
class StatusServiceTest extends PHPUnit_Framework_TestCase
{
    public function testGetAnalytics {
        $analytics = $this->statusService->getAnaltyics(1, strtotime("2012-01-01"), strtotime("2012-01-02"));
        //$analytics->setFacebook(new MockFacebook());

        // code truncated
    }
}
Verify that the test indeed fails when you run PHPUnit. Now, think about how we mocked an object and the method manually that we want to invoke. What did we do?
  1. The first step is to identify what objects to mock. In our analytics example feature above, we mocked the Facebook library. We are doing the same thing as a first step.
  2. Now that we have defined what class to mock, we have to know what methods in the class we want to mock specifying parameters and return values if there are any. A basic template I use in most cases go somewhat like this:
  1. Specify the number of times a method will be called (Required).
  2. Specify the method name (Required).
  3. Specify the parameters that the method expects (Optional).
  4. Specify the return value (Optional)
Let’s apply the steps just mentioned to our example test.
<?php
    //code truncated
    public function testGetAnalytics()
    {
        $arr = array(// facebook return);

        $facebook = $this->getMock('facebook') // object to mock
            ->expects($this->once())    // number of times to be called
            ->method('get') // method name
            ->with(1) // parameters that are expected
            ->will($this->returnValue($arr)); // return value

            // code truncated
    }
After we have created our mock facebook object again, inject it back again into our service:
<?php
    //code truncated
    public function testGetAnalytics()
    {
        $arr = array(// facebook return);

        $facebook = $this->getMock('facebook') // object to mock
            ->expects($this->once())    // number of times to be called
            ->method('get') // method name
            ->with(1) // parameters that are expected
            ->will($this->returnValue($arr)); // return value

        $this->statusService->setFacebook($facebook);

        // code truncated
    }
Now, you should have a passing test again. Congratulations! You have hit the ground running with using mock objects for testing! Hopefully you should be able to program more efficiently and most of all break free from show stopper dependencies you run into in the future. Image via Fotolia

Frequently Asked Questions (FAQs) about Mock Object Testing

What is the main purpose of using mock objects in testing?

Mock objects are primarily used in unit testing to isolate the system under test and ensure that the test only focuses on the code of the system. They simulate the behavior of complex, real objects and are therefore ideal for testing how a particular module of an application interacts with other modules. By using mock objects, you can control the indirect outputs of the behavior under test and confirm that the system behaves as expected.

How does mock object testing differ from traditional testing methods?

Traditional testing methods often involve testing the system as a whole, which can be time-consuming and complex. On the other hand, mock object testing allows you to test individual components of the system in isolation. This makes it easier to identify and fix bugs, as you can pinpoint exactly where the problem lies. Moreover, mock object testing allows you to simulate different scenarios and edge cases that might be difficult to reproduce with traditional testing methods.

Can you provide an example of how to use mock objects in testing?

Sure, let’s consider a simple example in Java using the Mockito framework. Suppose you have a class Calculator that depends on a service AddService to perform addition. You can create a mock of AddService and use it in your test for Calculator as follows:

import static org.mockito.Mockito.*;

public class CalculatorTest {
@Test
public void testAdd() {
// Create a mock object of AddService
AddService mockAddService = mock(AddService.class);

// Define the behavior of the mock object
when(mockAddService.add(10, 20)).thenReturn(30);

// Use the mock object in the Calculator
Calculator calculator = new Calculator(mockAddService);
int result = calculator.add(10, 20);

// Verify the result
assertEquals(30, result);
}
}

What are the benefits of using mock objects in testing?

Mock objects offer several benefits in testing. They allow you to isolate the system under test, making it easier to identify and fix bugs. They also enable you to simulate different scenarios and edge cases, which can be difficult to reproduce with traditional testing methods. Moreover, mock objects can make your tests run faster, as they eliminate the need for setting up and tearing down expensive resources like database connections.

Are there any drawbacks to using mock objects in testing?

While mock objects can be very useful in testing, they also have some drawbacks. One of the main drawbacks is that they can make your tests more complex and harder to understand, especially if you’re mocking complex objects or behaviors. Moreover, since mock objects only simulate the behavior of real objects, they might not accurately reflect the behavior of the real system in some cases. Therefore, it’s important to use mock objects judiciously and in combination with other testing methods.

How do I choose a mocking framework for my tests?

The choice of a mocking framework largely depends on your specific needs and the programming language you’re using. Some popular mocking frameworks include Mockito for Java, Moq for .NET, and Sinon.js for JavaScript. When choosing a mocking framework, you should consider factors like its ease of use, flexibility, documentation, and community support.

Can mock objects be used for integration testing?

While mock objects are primarily used for unit testing, they can also be used in integration testing to simulate the behavior of external systems that your application interacts with. However, it’s important to note that using mock objects in integration testing can make your tests less realistic, as they might not accurately reflect the behavior of the real external systems.

How do mock objects help in Test-Driven Development (TDD)?

In Test-Driven Development (TDD), you write tests before you write the actual code. Mock objects can be very useful in this approach, as they allow you to test your code in isolation, without having to implement the real objects that your code interacts with. This can make your development process more efficient and help you catch bugs early.

Can mock objects be used for performance testing?

Mock objects are not typically used for performance testing, as they do not accurately reflect the behavior of the real system. Performance testing usually involves testing the system as a whole, under realistic conditions, to measure its performance and identify any bottlenecks. However, mock objects can be used in performance testing to isolate specific components of the system and measure their performance in isolation.

What are some best practices for using mock objects in testing?

Some best practices for using mock objects in testing include: use them sparingly and only when necessary; avoid mocking complex objects or behaviors, as this can make your tests more complex and harder to understand; always verify the behavior of your mock objects to ensure they are being used correctly; and use mock objects in combination with other testing methods to ensure comprehensive coverage of your code.

Jeune AsuncionJeune Asuncion
View Author

Jeune has a fascination for building and solving things. Right now, he is interested in data mining, algorithms, performance, and software design. When not in front of his laptop, he cooks mean Asian dishes or is out kicking ass on the football (soccer) field.

Advanced
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week