PHPUnit: Mocking the System Under Test

When unit testing a class, at one point you’ll have to check if some functionality in a dependency is triggered. Usually this is done by replacing the dependency with a mock object. A well designed system let’s you inject dependencies in your objects, thus allowing for easier unit testing.

Mocking dependencies
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
class UserService
{
  proteced $mailerService;
  
  public function getMailerService()
  {
      if (empty($this->mailerService)) {
          throw new \RuntimeException('No mailer service set');
      }
      
      return $this->mailerService;
  }
  
  public function setMailerService(MailerInterface $service)
  {
      $this->mailerService = $service;
      
      return $this;
  }
  
  public function registerUser($userObject)
  {
      // …
      
      $mailerService = $this->getMailerService();
      $mailerService->sendMail('registration', $userObject);
  }
}


class UserServiceTest extends PHPUnit_Framework_TestCase
{

  public function testRegisteringUserTriggersMail()
  {
      $userObject = new User('Tom', 'tom@example.com');
  
      $mailerMock = $this->getMock('My\Namespace\MailerService');
      $mailerMock->expects($this->once())
          ->method('sendMail')
          ->with(
              $this->equalTo('registration'),
              $this->equalTo($userObject)
          );
          
      $userService = new UserService();
      $userService->setMailerService($mailerMock);
      
      $userService->registerUser($userObject);
  }

}

The example above tests if the mailer service is triggered when the registerUser() method is called. The System Under Test (SUT) here is the UserService, and the mock object is the MailerService.

But what if you wanted to mock one or more methods in the SUT itself? Like the example below:

Example with internal dependencies
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class UserService
{
    // ...
  
  public function registerUser($userObject)
  {
      $this->saveUser($userObject);
      
      $mailerService = $this->getMailerService();
      $mailerService->sendMail('registration', $userObject);
  }
  
  public function saveUser($userObject)
  {
      // ...
  }
}

This implementation of the UserService has 2 separate methods: one for saving a User object and a convenience method registerUser which calls saveUser and then sends out a mail.

I’ve sometimes wondered how you can make sure that the registerUser method calls the other method without actually covering the implementation of it. Let me rephrase that: “How can you mock a method in the SUT?”

Of course the answer was quite simple, but it only hit me after a while: Let the SUT become the mock!

In PHPUnit, it’s possible to create a partial mock. That’s a mock with not all its methods mocked. When you invoke a method of a partial mock that wasn’t mocked, then the original method is called. Here’s an example in a unit test:

Mocking the SUT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class UserServiceTest extends PHPUnit_Framework_TestCase
{

  public function testRegisteringUserCallsSaveUserMethod()
  {
      $userObject = new User('Tom', 'tom@example.com');
      
      $mock = $this->getMock('UserService', array('saveUser'));
      $mock->expects($this->once())
          ->method('saveUser')
          ->with($this->equalTo($userObject));
          
      $mock->registerUser($userObject);
  }
}

So first the SUT is mocked, with expectations for our saveUser method. Then on that mock we call the registerUser method, which is not mocked.

If you generate a code coverage report from that test, then only the registerUser method will be covered, which is exactly what I wanted.

Comments