A way to implement unit testing in Joomla!

Since a couple of months I’m not anymore self-employed and am I working for a company who develops for ActiveCollab, Magento and Joomla!. While developing I’m trying to work according the TDD paradigm. So you write your tests first and then you develop the business logic to succeed the failing test. I’d have to admit that Joomla! never will be my first choice to develop on, but it isn’t as bad some are preaching.

While getting my way around the Joomla! framework I have a big problem getting the checked in unit tests to run. If you do a checkout of the unit tests from the svn you’ll see there are about 960 tests and about 600 fails straight out of the box. Pretty annoying and unusable to write your own tests against.

So I’ve been working on a test runner of my own. The test runner tries to use the most of the Joomla! framework, but some things it just have to do itself. It consists of 2 files. One file is the testloader which is setup as an autoloader using the JLoad class. The second class sets some configuration files and kickstarts the Joomla! framework.

Here’s the code for the testloader


<?php

/**

* Testloader class

*

* This class strips the requested class and passes it thru

* to the Joomla autoload function

*

* @author Paul de Raaij <paul@paulderaaij.nl>

*/

// Preload the model so we can add paths to the inlude path of JLoader

jimport( 'joomla.application.component.model' );

class TestLoader {

/**

* Strip the given class and pass it to the

* Joomla autoloader.

*

* If it's a component add the path to the

* include path of the loader

*

* @param String $classname

* @return bool Has the inclusion succeeded

*/

static public function load( $classname ) {

$explodedPath = self::explodeClassname( $classname );

if( $explodedPath == null ) {

return self::loadJoomlaClass( $classname );

}

// Add the folder to the loader include path

JModel::addIncludePath('../components/com_' . $explodedPath['component'] . '/' . $explodedPath['type'] . 's');

return self::loadJoomlaClass( $classname );

}

/**

* Explode the classname and strip component name,

* type and type name

*

* @param string $classname

* @return mixed Returns null when it's a core file. Returns an array with data if it's a component data

*/

static protected function explodeClassname( $classname ) {

$types = array('model', 'controller', 'helper', 'view');

$classname = strtolower($classname);

if( $classname == 'jmodel' ) {

return null;

}

$fileInfo = array();

foreach( $types as $type ) {

if( false !== strpos($classname, $type) ) {

$fileInfo['type']       = $type;

}

}

if( isset( $fileInfo['type'] ) ) {

$fileInfo['component']  = substr($classname, 0, strpos($classname, $fileInfo['type'] ) );

$fileInfo['name']       = substr($classname, ( strpos($classname, $fileInfo['type'] ) + strlen($fileInfo['type']) ) );

return $fileInfo;

}

return null;

}

/**

* Simple pass thru function to the Joomla autoloader

*

* @param string $class

* @return bool Has inclusion succeeded?

*/

static public function loadJoomlaClass( $class ) {

return JLoader::load( $class );

}

}

?>

And this is the TestCase class which is required by each unit test file:


<?php

require_once 'PHPUnit/Framework.php';

// We are a valid Joomla entry point.

define('_JEXEC', 1);

// Setup the path related constants.

define('DS', DIRECTORY_SEPARATOR);

define('JPATH_BASE', dirname( __FILE__ ) . '\..');

define('JPATH_ROOT', JPATH_BASE);

define('JPATH_CONFIGURATION', JPATH_BASE);

define('JPATH_LIBRARIES', JPATH_BASE.DS.'libraries');

define('JPATH_METHODS', JPATH_ROOT.DS.'methods');

// Load the library importer, datbase class and configuration

require_once (JPATH_LIBRARIES.'/joomla/import.php');

require_once (JPATH_LIBRARIES.'/joomla/database/database.php');

// Load the TestLoader

require_once dirname(__FILE__) . '\lib\TestLoader.php';

// register specific test autloader

if( !defined('LOADED_AUTOLOADER') ) {

spl_autoload_register( array('TestLoader', 'load') );

define('LOADED_AUTOLOADER', true);

}

// Define configuration

jimport( 'joomla.registry.registry' );

require_once( JPATH_CONFIGURATION.'/configuration.php' );

// Create the JConfig object

$config = new JConfig();

// Get the global configuration object

$registry =& JFactory::getConfig();

// Load the configuration values into the registry

$registry->loadObject($config);

$options = array ('driver' => $config->driver,

'host' => $config->host,

'user' => $config->user,

'password' => $config->password,

'database' => $config->database,

'prefix' => $config->prefix );

// Preload database with configuration

$db = JDatabase::getInstance( $options );

?>

An example unit test looks like this:


<?php

require_once dirname(__FILE__) . '\..\..\..\TestCase.php';

class Model_Test_Base extends PHPUnit_Framework_TestCase {

protected $model = null;

public function setUp() {

$this->model = JModel::getInstance('<modelname>', '<componentname>Model');

}

public function testSimpleAssertion() {

$this->assertTrue( $this->model->test() );

}

}

?>

Perhaps this isn’t the best way to use unit testing in Joomla! But at the moment this is the only thing I got working. If you’ve got some suggestions your glad to leave it behind as a reaction!