Testing Codeigniter applications with PHPUnit

Motivation

Codeigniter had its glory days but becomes useless with time. On the other hand, there are some legacy systems it would be really nice if I could test’em (avoiding errors in production with upgrades and all). Codeigniter has its own unit testing library but I prefer PHPUnit.

Setup

Using Codeigniter version 2.2.0, Composer, PHPUnit 4.1.* e PHP 5.4.4. I’m considering you’ve installed Composer.

Steps

With the unpacked and working Codeigniter, create a new composer.json file with the following content:

{
  "require-dev": {
    "phpunit/phpunit": "4.1.*"
  }
}

Install PHPUnit on the project with the command:

composer.phar install

It’ll create a vendor folder with all the necessary packages for PHPUnit, including its binary under /vendor/bin/. Now if you run:

./vendor/bin/phpunit

you’ll see that PHPUnit works and shows its default help message.

Here we’ll create the PHPUnit config file for the project. Under the main folder, create a new phpunit.xml.dist file containing:

With PHPUnit you can write a boostrap file to set the test execution environment. For that, we’ll have almost the very same Codeigniter’s index.php saved in /tests/Bootstrap.php:

PHPUnit’s result comes os the terminal with a very pragmatic output. With that in mind, we’ll have to suppress the Codeigniter’s code output so when tests run we can understand what PHPUnit tests will tell us. To achieve it, we’ll use Codeigniter hooks.

First, change the /application/config/config.php file turning hooks on for the application:

<?php
  // ...
  $config['enable_hooks'] = TRUE;
  // ...

Later in /application/config/hooks.php refer to the hook we’ll use:

<?php
  // ...
  $hook['display_override'] = array(
    'class' => 'DisplayHook',
    'function' => 'captureOutput',
    'filename' => 'DisplayHook.php',
    'filepath' => 'hooks'
  );
  // ...

Lastly we’ll create the /application/hooks/DisplayHook.php hook:

PHPUnit has some incompatibilities with Codeigniter that we will have to handle. Change the framework’s core libraries is a very bad practice, so we’ll need to overwrite some Codeigniter libraries by copying to the /application/core/ folder with the _MY__ prefix on the file name. The first class will be the Utf8.php one.

cp /system/core/Utf8.php /application/core/MY_Utf8.php

Change the class name and the $CFG variable as follows:

<?php
  // ...
  class MY_Utf8 {
  // ...
    function __construct()
    {
      // ...
      // global $CFG;
      $CFG =& load_class('Config', 'core');
      // ...

The same thing must be done with the Output.php class:

cp /system/core/Output.php /application/core/MY_Output.php

Change the class name, the $CFG and $BM variables as follows:

<?php
  // ...
  class MY_Output {
    // ...
    function _display($output = '')
    {
      // ...
      // global $BM, $CFG;
      $CFG =& load_class('Config', 'core');
      $BM =& load_class('Benchmark', 'core');
      // ...

Now you’ll be able to run PHPUnit inside your project without errors:

taiar@guestxor:~/dev/ci$ ./vendor/bin/phpunit
PHPUnit 4.1.3 by Sebastian Bergmann.

Configuration read from /home/taiar/dev/ci/phpunit.xml.dist



Time: 100 ms, Memory: 2.25Mb

No tests executed!

So let’s write a test using Codeigniter! As stated before, PHPUnit will run tests from /tests/ folder. So we create the /tests/CITest.php file.

<?php

  class CITest extends PHPUnit_Framework_TestCase
  {
    private $CI;

    public function setUp()
    {
      // Load CI instance normally
      $this->CI = &get_instance();
    }

    public function testGetPost()
    {
      $_SERVER['REQUEST_METHOD'] = 'GET';
      $_GET['foo'] = 'bar';
      $this->assertEquals('bar', $this->CI->input->get_post('foo'));
    }
  }

Run for a successful test:

taiar@guestxor:~/dev/ci$ ./vendor/bin/phpunit
PHPUnit 4.1.3 by Sebastian Bergmann.

Configuration read from /home/taiar/dev/ci/phpunit.xml.dist

.

Time: 130 ms, Memory: 3.50Mb

OK (1 test, 1 assertion)

In this article I’ll not look into tests. Only the configuration of this environment given the initial motivation. Improvements on the tests execution would be done by mapping the Codeigniter error output to exceptions instead of the default html output and other issues. I don’t try any of this suggestions yet (it’s a little hard to be motivated when dealing with legacies).

References

comments powered by Disqus