Book Image

Instant Hands-on Testing with PHPUnit How-to

By : Michael Lively
Book Image

Instant Hands-on Testing with PHPUnit How-to

By: Michael Lively

Overview of this book

No developer wants to accept the inherent difficulty of writing software as an excuse for not finding the bugs in our code before anyone else does. PHPUnit is a framework that was created to allow developers to solve that very problem. It provides a feature-rich environment with most of the tools necessary to provide adequate tests for any project. "Instant Hands-on Testing with PHPUnit How-to" provides a thorough overview of the functionality provided by the PHPUnit framework. It shows how the plethora of features in the framework can be used to write tests for real world projects to ensure they function and will continue to function in the ways that you expect. This book will show how you can set up the scaffolding necessary to run unit tests in your project with PHPUnit. It will walk you through the process of how to write a basic test and how to maintain your project's test suite. You will learn how to use some of the more advanced features of PHPUnit and then see how you can use mock objects to isolate the code you are testing. We will then discover how to create tests that verify your interaction with databases and even see how you can use PHPUnit to understand which code you are actually testing. At the end of the book you will have all of the basic understanding necessary to begin adding tests to your project. This book provides a great foundation for becoming a expert at writing unit tests.
Table of Contents (7 chapters)

Testing abstract classes (Intermediate)


When we were discussing mock objects the concept of partial mocks was introduced. One common use of partial mocks is to test abstract classes. Abstract classes can't be tested directly as by definition they cannot be instantiated. You can always create an extension of the abstract class just for testing. However, PHPUnit provides functionality to very easily mock abstract classes so that only the abstract methods get mocked. All other functions will execute normally.

How to do it...

In src/Player.php is a Player class shown as follows:

<?php
abstract class Player
{
  // ...

  public function requestCard()
  {
    $cardNumber = $this->chooseCardNumber();

    if (!$this->hasCard($cardNumber))
    {
      throw new RuntimeException('Invalid card chosen by player');
    }

    return $cardNumber;
  }

  abstract protected function chooseCardNumber();

  // ...
}

The corresponding test can be placed in test/PlayerTest.php to test the abstract nature of the class.

<?php
class PlayerTest extends PHPUnit_Framework_TestCase
{
private $player;
  private $hand;

  public function setUp()
  {
    $this->hand = new CardCollection();
    $this->hand->addCard(new Card('A', 'Spades'));
    $this->player = $this->getMockForAbstractClass('Player', array('John Smith', $this->hand));
  }

  public function testRequestCardCallsChooseCardNumber()
  {
    $this->player->expects($this->once())
      ->method('chooseCardNumber')
      ->will($this->returnValue('A'));

    $this->assertEquals('A', $this->player->requestCard());
  }
}

How it works...

The PHPUnit method getMockForAbstractClass() can be used to generate a partial mock where only the abstract methods of a given class are overridden. The argument list for getMockForAbstractClass() is similar to the argument list for getMock(). The big difference is that the list of methods to mock is moved from being the second parameter to being the last parameter. By default getMockForAbstractClass() will mock only the abstract methods of the class. If you find yourself needing to override this functionality then you should just use getMock() instead.

In this example, the Player class is being mocked with a player name and a CardCollection object is being passed to the Player instance's constructor. The testRequestCardCallsChooseCardNumber() method is assuring that the Player::chooseCardNumber() method is called as a part of Player::requestCard() and is then ensuring that the value returned by chooseCardNumber() is subsequently returned by requestCard().

You could use getMock() for this instead. The setUp() method could be rewritten to use getMock() to set up the partial mock.

public function setUp()
{
  $this->hand = new CardCollection();
  $this->hand->addCard(new Card('A', 'Spades'));
  $this->player = $this->getMock('Player', array('chooseCardNumber'), array('John Smith', $this->hand));
}

The advantage of using getMockForAbstractClass() is that you do not have to add to the mocked method list (the second parameter of getMock()) every time you add a new abstract method to the class. It also keeps the test significantly more concise.

Abstract classes in Phake

Phake also provides a function that assists in testing abstract classes. Phake::partialMock() works in a similar fashion to the PHPUnit counterpart.

<?php
class PhakePlayerTest extends PHPUnit_Framework_TestCase
{
private $player;
  private $hand;

  public function setUp()
  {
    $this->hand = new CardCollection();
    $this->hand->addCard(new Card('A', 'Spades'));
    $this->player = Phake::partialMock('Player', 'John Smith', $this->hand);
  }

  public function testRequestCardCallsChooseCardNumber()
  {
    Phake::when($this->player)->chooseCardNumber()->thenReturn('A');

    $this->assertEquals('A', $this->player->requestCard());

    Phake::verify($this->player)->chooseCardNumber();
  }
}

The Phake::partialMock() method accepts the class name as the first parameter. The remaining parameters will be used in the constructor of the mock object. This method works in mostly the same way as getMockForAbstractClass(). It creates a mock that will call the original method for any non-abstract method.