Skip to main content

Quality Assurance on PHP projects - PHPUnit part 1

Of all tools available for improving quality assurance, there's one tool that is the core tool you have to master: PHPUnit. PHPUnit is a complete testing framework crafted by Sebastian Bergmann (@s_bergmann), who ported existing xUnit frameworks to PHP. And with this testing framework you're able to test your functionality in an automated way before you push code into production.

[editorial] As I cannot summarize the whole usage of phpunit in one blogpost, this will be a sequence of several articles that will pick up a specific task you want to cover with phpunit.

Well, first of all you need to have PHPUnit installed on your system. The easiest way to accomplish this is to use the PEAR installer.

user@server: $ pear channel-discover
user@server: $ pear channel-discover
user@server: $ pear channel-discover

Once you've got the right channels, you can install the framework:

user@server: $ pear install phpunit/PHPUnit

You can use three methods to configure the way PHPUnit executes tests on your project:
  • a configuration file
  • command line parameters
  • a combination of both
Using a configuration file called "phpunit.xml" is by far the easiest way to run your unit tests in a consistent and organized way. It's a simple XML configuration file and the following works for me most of the times.

NOTE: I use phpunit with a configuration file that matches my project settings, so you might need to modify these settings if you're using a framework or have a different source code layout.

<phpunit bootstrap="./TestHelper.php" colors="true">
    <testsuite name="Unit test suite">

            <directory suffix=".php">../src/</directory>

This simple configuration file has 3 major parts:

1. The phpunit configuration tag
This tag starts the complete configuration, but also includes a bootstrap file (for bootstrapping your application like Zend Framework's bootstrapper class) and enables colors to show green, yellow and red colored bars to indicate the status of your tests. Since I'm a fan of a warm, fuzzy feeling whenever I see green, I enable this by default.

2. Testsuite section
The testsuite is a wrapper that bundles all your tests into a single suite, so you can call it immediately at execution. You can provide it with a name and a directory.
This section is very useful if you have multiple sections in your projects that you want to separate, as you can define the test suites based on their paths on the filesystem.

3. Filtering
Filtering allows you to quickly define what needs to be tested and what needs to be excluded. In my case I need just to investigate my Zend Framework application path and my custom library path. I also want to exclude all my templates as they don't contain any logic.

You can add also a section for logging where you can define code coverage reports (requires XDebug), a testdox progress report and other kinds of reports provided by PHPUnit. But since I will transfer these tasks to another tool, I'm not going to discuss it here.

I also use a bootstrap file for phpunit called TestHelper.php that allows me to "bootstrap" my test suite. This bootstrap file sets my defined constants, include paths, timezone and error reporting levels.

This is a very minimalistic bootstrapper:
// file: tests/TestHelper.php

// set our app paths and environments
define('BASE_PATH', realpath(dirname(__FILE__) . '/../'));
define('APPLICATION_PATH', BASE_PATH . '/src');
define('TEST_PATH', BASE_PATH . '/tests');
define('APPLICATION_ENV', 'testing');

// Include path
    . PATH_SEPARATOR . get_include_path()

// Set the default timezone !!!

// We wanna catch all errors en strict warnings

So how do you start?
Testing is really simple but for most people the complexity of having code testing other code and just report back if it's working or not is a bit confusing for people that haven't written any tests before.

For the most part, in my professional career I get a lot of excuses like "no time", "no budget" or "no interest". If it's truly such a big problem to cope with something as simple as writing a test, a career change might be in order. And I mean it. I believe that if you're serious about your job, you have to take a little responsibility in it. And writing tests is not that hard.

Let's begin with a classic example: a tic-tac-toe game. This is a simple pen-and-paper game for two players (O and X) that uses a grid of 3 by 3. The purpose of the game is to have a row, a column or a diagonal row filled with only O's or X's. More information can be found at


So how do you start with this kind of a game? Very simple: define the objects, the rules and the goal of this game in a list that makes sense to you. I like to define it as follows:
  • objects:
    • symbol X for user 1
    • symbol Y for user 2
    • play grid of 3 rows by 3 columns
  • rules:
    • write your symbol once for each turn within the grid
  • goal:
    • prevent opponent to win
    • win by having 3 symbols on a single row, a single column or a single diagonal row
Since I have tendency to write my tests before I write the code, as it allows me to think about what I expect I should get from class methods. Another benefit is that you write smaller classes with methods that just do what needs to be done.

Let's define our general outline for our main class Tictactoe. We already know a few things: two players, a grid of 3 rows by 3 columns and a winner when three identical symbols are in a single row horizontal, vertical or diagonal.

In test code it would look something like this:
// file: tests/TictactoeTest.php
require_once 'Tictactoe.php';
class TictactoeTest extends PHPUnit_Framework_TestCase
    public function testGameGridIsSetAtStart()
        // test to see a grid is created of 3 by 3 and all fields are null
    public function testGamePlayersAreSetAtStart()
        // test to see players are set up at start with symbols X and O
    public function testGameCanBePlayed()
        // play a simple game with two players turn by turn
        // returning true or false would notify us if there's a winner or not

We test 3 major things here: that a grid is set, that two players are created and that the game can be played. We don't worry for edge cases or exceptions now, as we can take care of that later.

When we run this test, we get a bunch of errors, basically saying that we don't have a Tictactoe class file yet. At this point, this is very normal.

user@server:/dev/ttt/tests $ phpunit
PHP Warning:  require_once(Tictactoe.php): failed to open stream: No such file or directory in /dev/ttt/tests/TictactoeTest.php on line 3

Warning: require_once(Tictactoe.php): failed to open stream: No such file or directory in /dev/ttt/tests/TictactoeTest.php on line 3
PHP Fatal error:  require_once(): Failed opening required 'Tictactoe.php' (include_path='.:/dev/ttt/src:.:/usr/lib/php/pear') in /dev/ttt/tests/TictactoeTest.php on line 3

Fatal error: require_once(): Failed opening required 'Tictactoe.php' (include_path='.:/dev/ttt/src:.:/usr/lib/php/pear') in /dev/ttt/tests/TictactoeTest.php on line 3

We've got are main goals set, now it's time to make some assertions to see we get the information we want.

First of all, we're going to setUp and tearDown our test case, so we know each test starts with a clean slate.

class TictactoeTest extends PHPUnit_Framework_TestCase
    protected $_ttt;

    protected function setUp()
        $this->_ttt = new Tictactoe();
    protected function tearDown()
        $this->_ttt = null;

Now we need to fill in the blanks regarding the test methods. Let's start with our first method: 

    public function testGameGridIsSetAtStart()
        // we should be able to retrieve the grid from our Tictactoe class
        $grid = $this->_ttt->getGrid();

        // let's make sure we created a grid class to handle all grid related manipulations
        $this->assertInstanceOf('Grid', $grid);

        // we know the grid is 3 x 3, so let's count the rows
        $this->assertEquals(3, count($grid->getRows()));

        // and for each row, let's count the columns
        // and since we're at it, let's check that their fields are empty
        foreach ($grid->getRows() as $row) {
            $this->assertInternalType('array', $row);
            $this->assertEquals(3, count($row));
            foreach ($row as $column) {

So, what do we learn from this test? We need to have a Grid class to take care of all grid related things and we need to ensure each field is empty before we start playing. Take a note of this as we need it later!

The next step is to set up our players. We know we have two players and each player has his own symbol (X or O).

    public function testGamePlayersAreSetAtStart()
        // we should be able to retrieve all players from our Tictactoe class
        $players = $this->_ttt->getPlayers();

        // ensure that players have their own class to take care of all players
        $this->assertInstanceOf('Players', $players);

        // we know we only have two players
        $this->assertEquals(2, count($players));

        // we want to ensure that each player has the correct symbol
        $this->assertEquals(Player::PLAYER_X, $players->seek(0)->current()->getSymbol());
        $this->assertEquals(Player::PLAYER_O, $players->seek(1)->current()->getSymbol());

This test teaches us we need another two classes for our players. One class is acting as a collection implementing the SeekableIterator and Countable interfaces from SPL (I just love these, so I know I will use them!). The second class defines the player and the symbol it gets.

Our last test method is all about playing the game.

    public function testGameCanBePlayed()
        // let's pick just one player
        $player = $this->_ttt->getPlayers()->seek(0)->current();

        // and see if we can win if we created a row
        $this->assertFalse($this->_ttt->play(0, 0, $player));
        $this->assertFalse($this->_ttt->play(0, 1, $player));
        $this->assertTrue($this->_ttt->play(0, 2, $player));

        $this->_ttt->setGrid(new Grid());

        // and see if we can win if we created a column
        $this->assertFalse($this->_ttt->play(0, 1, $player));
        $this->assertFalse($this->_ttt->play(1, 1, $player));
        $this->assertTrue($this->_ttt->play(2, 1, $player));

        $this->_ttt->setGrid(new Grid());

        // and see if we can win if we go diagonal left to right
        $this->assertFalse($this->_ttt->play(0, 0, $player));
        $this->assertFalse($this->_ttt->play(1, 1, $player));
        $this->assertTrue($this->_ttt->play(2, 2, $player));

        $this->_ttt->setGrid(new Grid());

        // and see if we can win if we go diagonal right to left
        $this->assertFalse($this->_ttt->play(0, 0, $player));
        $this->assertFalse($this->_ttt->play(1, 1, $player));
        $this->assertTrue($this->_ttt->play(2, 2, $player));

In my next article we're going to set up our classes that should turn this test into a success. In the mean time, I challenge you to figure out how the code might look like. And if you want, share that code with everyone by posting it on, or using github's gist.


  1. Anonymous17/8/11 13:43

    Thank you. Thank you very much! Please don't stop. I want to see how to build an entire project from scratch using TDD (including handling views). I've never achieve this :(
    Thank you!


Post a Comment

Popular posts from this blog

Deploy Docker containers fast to Microsoft Azure

DEPLOY DOCKER CONTAINERS FAST TO MICROSOFT AZURE It’s hard to ignore the fact thatDockeris a way to move forward for rapid application development, distributed architectures and microservices. For developersDockeroffers great advantages as they can build their containers specifically for the task they work on. They grab a base image of a container, modify it for their purpose and prepare the functionality inside the container. Quality, testing and security teams now have a single instance to look at and ensure all functional and regulatory requirements are met. System engineers now don’t have to worry about providing a system with the required specs as the container is already provisioned for that purpose. But where do you deploy yourDockercontainers? You can set up your existing bare metal infrastructure to allow them to run containers, but this also means you need to learn about securing your container infrastructure, which is not an easy task. Luckily “the cloud” offers container …

Speeding up database calls with PDO and iterators

When you review lots of code, you often wonder why things were written the way they were. Especially when making expensive calls to a database, I still see things that could and should be improved.
No framework development When working with a framework, mostly these database calls are optimized for the developer and abstract the complex logic to improve and optimize the retrieval and usage of data. But then developers need to build something without a framework and end up using the basics of PHP in a sub-optimal way.

$pdo = new \PDO( $config['db']['dsn'], $config['db']['username'], $config['db']['password'] ); $sql = 'SELECT * FROM `gen_contact` ORDER BY `contact_modified` DESC'; $stmt = $pdo->prepare($sql); $stmt->execute(); $data = $stmt->fetchAll(\PDO::FETCH_OBJ); echo 'Getting the contacts that changed the last 3 months' . PHP_EOL; foreach ($data as $row) { $dt = new \DateTime('2015-04-…

PHP Arrays - Associative Arrays or Hash Maps

Associative array or hash maps are listings of key and value pairs with a posibility to nest additional keys and values. An associative array is a very powerful construct within PHP.

In our previous article we discussed simple arrays, which in their turn are indexed associative arrays under the hood. Take the following example:

$array = [

Is in fact an indexed associative array under the hood:

$array = [
0 => 'apple',
1 => 'banana',
2 => 'chocolate',

But associative arrays can be so much more than just an indexed array, and you will find many database operations returning arrays where the fields of a table are the keys in the array while their values are also the values within the array.

$productRowData = [
'product_id' => 1234,
'brand_id' => 321,
'product_name' => 'Our awesome product',
'prodcut_description' => 'This is our most awesome product.&#…