Unit testing for phpBB

I was writing a MOD for phpBB, and at some moment I found I can't go further without unit testing. I found it's quite a hard task because phpBB is written without testing in mind. However, unit testing for phpBB is possible.

The beginning of a test file consists of two parts:

* loading SimpleTest stuff, and
* loading phpBB stuff.

//
// SimpleTest
//
define('TEST', __FILE__);
define('SIMPLE_TEST', dirname(TEST) . '/simpletest');
require_once(SIMPLE_TEST . '/unit_tester.php');
require_once(SIMPLE_TEST . '/reporter.php');
//
// phpBB
//
define('PHPBB_PATH', dirname(TEST) . '/../phpBB2');
ini_set('include_path', '.:' . PHPBB_PATH . ':' . ini_get('include_path'));
define('IN_PHPBB', 1);
require_once('extension.inc');
require_once('common.php');
require_once('includes/functions_tc.php');

The first step is to get the path to the phpBB installation. The path is hardcoded.

The next step is to define the include path. The code adds the phpBB path to the include path and, what's more important, adds the current directory before everything else. It helps to hijack loading of a phpBB file and substitute it with a special version of the file.

The most of the phpBB files loads, explicitely or not, "extension.inc" and "common.php". Before loading them, the constant "IN_PHPBB" should be defined, otherwise you'll get the "hacking attempt" error.

Finally, I load my file "includes/functions_tc.php" to test its functions.

What's behind the scene is the mock file "includes/db.php", which is loaded by phpBB instead of the orignial "includes/db.php", thanks to the carefully set include paths. Here is the current version of the file:

<?php
//
// Mock database object
//
class db_mock {
  var $data;
  function db_mock($data) {
    $this->data = $data;
  }
  function sql_query() {
    return 1;
  }
  function sql_fetchrow() {
    return 0;
  }
  function sql_fetchrowset() {
    reset($this->data);
    return $this->data;
  }
  function sql_freeresult() {
  }
  function sql_affectedrows() {
    return 0;
  }
  function sql_error() {
    $a = debug_backtrace();
    var_dump($a);
    $s = '';
    foreach ($a as $item) {
      $s2 = "--\n";
      foreach ($item as $k=>$v) {
        $s2 .= "$k = $v\n";
      }
      $s .= $s2;
    }
    $s .= "--\n";
    return $s;
  }
}

$db = new db_mock(array());
?>

An object of this class just returns the data you want, all other methods are just stubs to do nothing.

Normally, the call to sql_error shouldn't happen. However, it happens, mostly when the developer of tests overlooked something. To help find the error, I convert the stack trace to a string and return it. Unfortunately, this string is lost in the phpBB debrees, therefore I use var_dump to print the trace to the console. Returning the string isn't needed anymore, but I left it as a legacy code :-)

Testing admin scripts requires yet another trick. Before loading them, add something like:

$setmodules = 1;
$module     = array();
require_once('admin/admin_tc.php');

The admin script decides that it's started by the information collector, therefore the script updates the array $module and exits immediately. But thanks to the PHP features, all the functions, defined in the script file, are now available.

Well, does all this stuff worth the efforts? Yes.

* After I had written the tests, I found 3 errors in my good code, and one error was serious.
* I can modify the code without being afraid to break something.
* I can test the code without clicking and typing in browser, and without repeating everything each time after new changes.
* It have saved me a lot of time and efforts.

Categories: PHP

Updated: