Unit testing 4: Mock objects and testing code which uses the database

June 26, 2009 – 6:13 pm Tags: ,

After learning to write tests and some good testing practices, it’s now time to look at mock objects.

When testing a class which needs an instance of another class to work, you do not want to depend on the other class too much. This is where mock objects come in – a mock object is a “clone” of an object, which we can use to simplify our tests, by having the mock object perform assertions or by replacing some functionality of the mock with our custom functionality.

In this post we’ll first look at how mock objects are created and used with PHPUnit, and then we’ll take a practical example of using mocks to test code which uses the database to fetch some data.

Mock basics

Before we look at how PHPUnit does mocks, let’s first check out the principle of how the mocks work.

You can create mocks even without the fancy methods PHPUnit provides to you – by simply creating a new class which extends the class you wish to mock:

class SomeClassMock extends SomeClass {
}

We could call that a mock object, although it’s not very useful yet.

Say the class which we are testing needs to call a method in SomeClass, and we want to be sure it actually gets called in our test. In this case, let’s add a variable and a method to our mock class:

class SomeClassMock extends SomeClass {
  public $methodWasCalled = false;
 
  public function aMethod() {
    //This method should get called by the object we are testing
    $this->methodWasCalled = true;
  }
}

Now, in our test, we can do something like this:

//Just a simple example
public function testAMethodIsCalled() {
  //First, create a mock of SomeClass
  $someClass = new SomeClassMock();
 
  //This is the object we are testing, let's assume
  //it takes a SomeClass instance as the parameter
  $object = new MyClass($someClass);
 
  //Say the method in SomeClass needs to be called now
  $object->doSomething();
 
  //Confirm the method was called by checking the mock's variable:
  $this->assertTrue($someClass->methodWasCalled);
}

Creating mock objects with PHPUnit

Writing mock objects like we saw above is quite time consuming and complex. Luckily, we don’t have to actually write the mock classes ourselves, as PHPUnit provides us with a set of methods we can use to do almost any kind of mock we need. We will use the PHPUnit mocking API from now on, but it’s always useful to know how the things work behind the scenes.

Let’s rewrite the test with PHPUnit’s mocking API:

public function testAMethodIsCalled() {
  //First, create a mock of SomeClass
  $someClass = $this->getMock('SomeClass');
 
  //Now we can tell the mock what we want to do:
  $someClass->expects($this->once())
            ->method('aMethod');
 
  //This is the object we are testing, let's assume
  //it takes a SomeClass instance as the parameter
  $object = new MyClass($someClass);
 
  //Say the method in SomeClass needs to be called now
  $object->doSomething();
 
  //We don't need to do anything else - the mock object will confirm
  //that the method was called for us.
}

Doesn’t look too complex does it? Instead of writing a whole class to do a simple method-call check, we just use the getMock method to receive a mock-version of an object, and then we tell it what it needs to do in this test.

What did we do to the mock?

First, we call the expects method and pass $this->once() as a parameter. Then, we call method with 'aMethod'. This means that the mock object “expects one method call to aMethod”. Replacing a method in the mock like this is also known as stubbing the method – the code in the method is replaced by a “test stub”.

What will happen if the method does not get called? You will get an error similar to this when you run your test:

1) testAMethodIsCalled(ExampleTest)
Expectation failed for method name is equal to <string:aMethod> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.

Using mocks to test database code

Now that we know our mocks, or at least enough to start with, let’s jump into some useful examples.

A very typical case is that you need to test code which uses the database – for example, the code might be fetching some data from the DB, or maybe inserting something. We certainly could have a testing database, but it’s much easier and simpler to work with mock objects.

The example scenario

Let’s say we have a class called EventRepository which tracks some kind of events in our application. We also have a class called Event, which represents a single event. We now want to write unit tests for the EventRepository class, but as the class accesses the database we have a dilemma.

class EventRepository {
  private $_pdo;
 
  public function __construct(PDO $database) {
    $this->_pdo = $database;
  }
 
  public function findById() {
    $sql = 'SELECT * FROM events WHERE ...';
 
    //Here we have some code to execute the SQL, convert
    //the result to an Event object and then return it
  }
 
  /* some other methods here */
}

To test this class, we would need to use a test database. This is because the SQL queries etc. are executed inside the class and there’s nothing our test can do about it.

Making the EventRepository testable

To make this class better, and easier to test, let’s modify it to use a data access object.

The data access object (DAO) will look something like this for our example:

class EventDao {
  private $_pdo;
 
  public function __construct(PDO $pdo) {
    $this->_pdo = $pdo;
  }
 
  public function findById() {
    $sql = 'SELECT * FROM events WHERE ...';
 
    //Instead of event repository, the code to execute SQL is here.
    //We then return the row's data as an array
  }
 
  /* some other methods here */
}

Then, the EventRepository is modified to use EventDao:

class EventRepository {
  private $_dao;
 
  public function __construct(EventDao $dao) {
    $this->_dao = $dao;
  }
 
  public function findById($id) {
    $row = $this->_dao->findById($id);
 
    //Now we simply process $row into an Event object. No direct DB access
  }
 
  /* some other methods here */
}

Testing the thing

Now we can test the class! We mock out the EventDao object, so the testing code for EventRepository will not need access to a database.

Let’s write a test for the findById method to demonstrate mocking the DAO:

public function testFindsCorrectEvent() {
  //Set up a fake database row
  $eventRow = array(
    'id' => 1,
    'name' => 'Awesome event'
  );
 
  $dao = $this->getMock('EventDao');
 
  //Set up the mock to return the fake row when findById is called
  $dao->expects($this->once())
      ->method('findById')
      ->with(1)
      ->will($this->returnValue($eventRow));
 
  $repo = new EventRepository($dao);
 
  $event = $repo->findById(1);
 
  //Confirm ID of the returned Event is correct
  $this->assertEquals(1, $event->getId());
}

Here we have some more features of the mock API – checking parameters and deciding return values.

Firstly, the $eventRow is a “fake” return value for the mocked dao. For the repository to work correctly, the dao will need to return it a value, and this is it.
Expects and method in the mock set up is the same as before, but the with(1) and will($this->returnValue($eventRow)) are new.

with is used to tell the mocked method what parameters it expects, in this case it should be passed the event’s ID: 1. If it is not passed the value 1 when called, it will report the test as a failure.
will is used to tell the mocked method what it should do when it gets called, and we want it to return our fake row.

The rest of the test sets up the repository that we are actually testing, and calls a method and asserts that the return value was correct. We didn’t go over the rest of the code in findById, nor did we give Event a getId() method, but just assume that there was some code in findById which turns the row into an Event object, and that Event has the method.

Summing up and further reading

Using the PHPUnit mocking API we can easily replace dependencies in our tested classes with mocks that make testing much simpler, than having to depend on the actual object itself.

We didn’t cover all possible mock calls, such as how to have it throw an exception. For that and more, I recommend reading the PHPUnit manual stubs and mocks chapter.

In the next post, we’ll check out how to do test-driven or test-first style development

Share this:
  1. 21 Responses to “Unit testing 4: Mock objects and testing code which uses the database”

  2. Thanks a lot, a verry well written tutorial, it maked quite a few things clearer for me since i had some troubles understanding how to run tests against models since they are connected to the database.

    By solomongaby on Jun 26, 2009

  3. Great article, thanks, Jani!

    By Adam on Jun 28, 2009

  4. How do you test the DAO?

    By Paul on Jun 29, 2009

  5. It is nice to make a class like a Repository more testable, but it is a useful abstraction to put a Repository between the Dao and the application code? The Dao is already an abstraction. However, from the point of view of testing patterns it is all correct.

    By Giorgio Sironi on Jun 29, 2009

  6. I enjoyed reading these articles. Great work, Jani!

    I’m currently trying to apply unit testing in one of my pet Zend Framework projects. A zf contains controllers and models/other classes that require unit tests. I’ve seen many examples describing how to create unit tests for controllers. However, I haven’t seen (or I have missed those) articles that describe how to create a testing facility that tests both controllers and models/other classes in an “good” way.

    For example, when testing an action that results in a new user been created, how is one supposed to create tests for such event? The controller tests should probably verify that the action redirects properly depending on the success of the request etc but I might not want to hit the real db during the tests.

    Do you have any insight on this?

    By Janimatti Ellonen (jme) on Jul 24, 2009

  7. @Janimatti: with Zend_Test you can do integration tests on the controllers and provide a fake db because the bootstrap of the test case is customizable. Doing real unit test on the controllers may not be useful as ideally they should be very thin, with all the login in the underlying domain layer.

    By Giorgio Sironi on Jul 24, 2009

  8. @Giorgio Sironi:

    The fake db might be a good idea, yes. I’m just a bit confused about the whole picture regarding testing controllers and models.

    Thanks for the hint.

    By Janimatti Ellonen (jme) on Jul 24, 2009

  9. As Giorgio pointed out, the usual approach would be to mock dependencies such as the database. Depending on how your code works, it could be that you put mocks into a registry, pass them as params in the request or some other form of injecting them into the controller.

    I’m planning on doing a post on testing ZF controllers, but that is a bit delayed currently since it’s a bit too time consuming right now.

    By Jani Hartikainen on Jul 24, 2009

  10. Hi!
    I’m studying right now how mock objects works and your post, and the previous articles, are excellent.

    Just one thing. I have tried your example but when i run the test it fails because Mock_EventDao wants an argument (especially a PDO instance).
    I have tried to create a Mock PDO to pass to my Mock_EventDao but in turn it asks for parameters so a solution is to create other mocks and so on..

    This problem can be recurrent and i think it’s not a good way to create mocks and mocks not relevant for the specific testcase but useful only to satisfy Mock_EventDao dependency..

    Is it the solution or is there a clean way?

    i hope i have explained well the doubt ;)

    By edo on Jul 29, 2009

  11. There’s a mistake in the sample, will have to correct it. Look at the getMock docs, you can either disable the constructor or add the argument.

    The easiest way to solve the problem you indicate is probably to use the getMock parameter which also mocks the constructor of the mock object, thereby removing anything you would typically need to pass it when creating it. This means you won’t need to create any dependencies for your mocked objects.

    By Jani Hartikainen on Aug 2, 2009

  12. Awesome tutorial – helped me A LOT with PHPUnit database testing

    By Houen on Feb 22, 2010

  13. Here’s running code of what the article’s first example might look like, for those still confused. (put the following in MockTest.php):

     
    class SomeClass {
        public function aMethod() {
            echo "hi from aMethod\n";
        }
    }
     
    class MyClass {
        public function __construct(SomeClass $someClass) {
            $this->SomeClass = $someClass;
        }
        public function doSomething() {
            echo "hi from doSomething\n";
            $this->SomeClass->aMethod();
        }
    }
     
    class MockTest extends PHPUnit_Framework_TestCase {
        public function testAMethodIsCalled() {
            $someClass = $this->getMock('SomeClass');
     
            $someClass->expects($this->once())
                      ->method('aMethod');
     
            $object = new MyClass($someClass);
     
            // $someClass->aMethod();
     
            $object->doSomething();
        }
    }

    By Ryan on Feb 22, 2011

  14. Thanks for the example Ryan. I fixed the -> for you :)

    By Jani Hartikainen on Feb 22, 2011

  15. I will repeat Paul’s question: how do you test EventDao then?

    By marines on May 8, 2012

  16. @marines I must’ve missed his question – in any case, in order to test the DAO, you would need to set up an actual integration test with a database.

    For example, you could use the PHPUnit database helper to set up an in-memory SQLite database for your tests, which is probably the simplest way as you don’t need an actual DB server.

    The basic order of business for such a test would be:

    1. Before running the test, set up the database state
    2. Perform some actions against the DB with the system under test
    3. Compare database state with expected state after step 2

    By Jani Hartikainen on May 8, 2012

  17. Hi,

    I don’t get completely those mock objects. What is the point of test if the object always will return good values, because they are hardcoded? I mean

    $eventRow = array(
    ‘id’ => 1,
    ‘name’ => ‘Awesome event’
    );

    those values are hardcoded, so mock object will always return them and our test will succeed. Only it will fail if we change those values. How do we then find bugs or soemthing from this test?

    By SPeed_FANat1c on May 24, 2012

  18. That’s a good question.

    The idea of a mock object is to make the test not depend on the other class’s behavior (and possible bugs in it!).

    One test scenario should always be written for each real usage scenario. A mock object returning good data is only one possible scenario. It will help you detect bugs in the code which handles the good scenario.

    You should also include a test which has a mock object returning bad data. This in turn will allow you to test the behavior of the code in an erroneous situation.

    So why is the data hardcoded? Why not return dynamic or random data? The idea of a unit test is to be reproducible. If a test fails, we should be able to just run it again and reproduce the error.

    This is why the data is hardcoded. If the data was dynamic, it would be hard to reproduce a failing test when the test failing would depend on the value it was given.

    By Jani Hartikainen on May 24, 2012

  1. 4 Trackback(s)

  2. Jun 29, 2009: Jani Hartikainen’s Blog: Unit testing 4: Mock objects and testing code which uses the database | DreamNest - Technology | Web | Net
  3. Jun 29, 2009: Jani Hartikainen’s Blog: Unit testing 4: Mock objects and testing code which uses the database | Webs Developer
  4. Dec 3, 2009: Unit testing in PHP | Behind The View
  5. Mar 22, 2012: Unit Testing | The Eye of Saargus

Post a Comment

You can use some HTML (a, em, strong, etc.). If you want to post code, use <pre lang="PHP">code here</pre> (you can replace PHP with the language you are posting)