phpunit Test Doubles (Mocks and Stubs) Simple mocking


Example

Introduction

The PHPUnit Manual describes mocking as such:

The practice of replacing an object with a test double that verifies expectations, for instance asserting that a method has been called, is referred to as mocking.

So instead of stubbing out code, an observer is created that not only replaces the code that needs to be silenced, but observes that a specific activity would have happened in the real world.

Setup

Let's start with a simple logger class that for the sake of clarity, simply displays the text sent into the parameter (normally it would do something that can be problematic to unit testing, such as updating a database):

class Logger {
    public function log($text) {
        echo $text;
    }
}

Now, let's create an Application class. It accepts a Logger object as a parameter to the run method, which in turn invokes the Logger's log method to capture that the application has started.

class Application {
  public function run(Logger $logger) {
    // some code that starts up the application

    // send out a log that the application has started
    $logger->log('Application has started');
  }
}

If the following code was executed as written:

$logger = new Logger();
$app = new Application();
$app->run($logger);

Then the text "Application has started" would be displayed as per the log method inside of the Logger.

Unit Testing with Mocking

The Application class unit testing does not need to verify what happens within the Logger log method, it only needs to verify that it was called.

In the PHPUnit test, an observer is created to replace the Logger class. That observer is set up to ensure that the log method is invoked only once, with the parameter value "Application has started".

Then, the observer is sent into the run method, which verifies that in fact the log method was called just once and the test case passes, but no text was displayed.

class ApplicationTest extends \PHPUnit_Framework_TestCase {

  public function testThatRunLogsApplicationStart() {

    // create the observer
    $mock = $this->createMock(Logger::class);
    $mock->expects($this->once())
        ->method('log')
        ->with('Application has started');
    
    // run the application with the observer which ensures the log method was called
    $app = new Application();
    $app->run($mock);
  
  }
}