12

I've asked this question on StackOverflow, and it was suggested that I ask it here.

I'm familiar with unit/system/integration testing, and would like to be able to test my Joomla component. Is there a standard way of doing this?

I'm working through this joomla mvc component example, which doesn't include tests. All I can find in the Joomla documentation and on various websites is fragments of test code & bootstrap.php files. Specifically what I would like to know is:

  • Where to put component test code
  • Do I need to provide my own bootstrap.php, or is there some way to just 'include joomla' and run my tests

Ideally someone could direct me to an open source Joomla component that has tests & instructions on how to run them (had a quick look, first 5 or so didn't have tests).

The closest I've found is this, which I've based my dummy test on.

What I've done so far

Component directory structure:

  • helloworld/
    • admin/
      • ...
      • tests/
        • bootstrap.php
        • phpunit.xml
        • modelHelloWorldsTest.php
    • site/
      • ...
    • helloworld.xml

First attempt

To run the tests, I install/copy the component to my Joomla installation. I then run the following command from ~joomla/administration/components/com_helloworld/tests:

php phpunit-4.2.phar --bootstrap bootstrap.php .

from which I receive

Fatal error: Class 'ContentController' not found in C:\inetpub\wwwroot\ws_cairnstest\administrator\components\com_helloworld\tests\modelsHelloWorldsTest.php on line 5

I gather this means my bootstrap.php isn't correct and hasn't loaded the necessary Joomla classes. I'll investigate this at some point, but this seems like a lot of setup just to get some tests to run.

bootstrap.php:

<?php
error_reporting(E_ALL);

define('_JEXEC', 1);
define('BASEPATH',realpath(dirname(__FILE__).'/../../'));
define('JOOMLA_PATH',realpath(dirname(__FILE__).'/../../../../../'));
define('JOOMLA_ADMIN_PATH',realpath(dirname(__FILE__).'/../../../../'));
$_SERVER['HTTP_HOST'] = 'localhost';
$_SERVER['REQUEST_METHOD'] = 'GET';

if (file_exists(JOOMLA_ADMIN_PATH . '/defines.php'))
{
    include_once JOOMLA_ADMIN_PATH . '/defines.php';
}

if (!defined('_JDEFINES'))
{
    define('JPATH_BASE', JOOMLA_ADMIN_PATH);
    require_once JPATH_BASE . '/includes/defines.php';
}

require_once JPATH_BASE . '/includes/framework.php';
require_once JPATH_BASE . '/includes/helper.php';
require_once JPATH_BASE . '/includes/toolbar.php';
define('JPATH_COMPONENT',JOOMLA_ADMIN_PATH.'/components/com_content');
$app = JFactory::getApplication('administrator');
include BASEPATH.'/controller.php';

modelsHelloWorldsTest.php:

<?php
class HelloWorldsTest extends \PHPUnit_Framework_TestCase {

    public function testList(){
        $c = new ContentController();
        $model = $c->getModel('helloworlds');
        $worlds = $model->getItems();
        var_dump($worlds);
        $this->assertNotEmpty($worlds);
    }
}

phpunit.xml:

<phpunit bootstrap="bootstrap.php"
    colors="true"
    convertErrorsToExceptions="true"
    convertNoticesToExceptions="true"
    convertWarningsToExceptions="true"
    processIsolation="false"
    stopOnFailure="false"
    syntaxCheck="false"
    verbose="true">
</phpunit>

Second attempt

After seeing this answer, I put my tests under test/unit under my joomla installation, copied phpunit.dist.xml and bootstrap.php from the joomla-cms repo to their appropriate locations, and still got the

Fatal error: Class 'ContentController' not found in C:\inetpub\wwwroot\ws_cairnstest\administrator\components\com_helloworld\tests\modelsHelloWorldsTest.php on line 5

error I was receiving before.

3

Where to put component test code

Typically best practices says:

/src/
/tests/

But I skipped the /src and simply but /tests/ inside the component directory. On the front end, for an "abc" component, it might look something like:

/components/com_abc/
/components/com_abc/abc.php
/components/com_abc/controller.php
/components/com_abc/router.php
/components/com_abc/tests/
/components/com_abc/tests/controllerTest.php

Do I need to provide my own bootstrap.php

I did. Sort of. I just copied the goodies in the root index.php file to the top of my unit test script. Yours was close to mine. My next step would be to encapsulate into a separate file that I would require_once().

<?php

error_reporting(E_ALL);

define('_JEXEC', 1);
define('_PHPUNIT', 1);
define('JPATH_BASE', "/var/www/html/abcsite");

require_once JPATH_BASE . '/includes/defines.php';
require_once JPATH_BASE . '/includes/framework.php';

// Instantiate the application.
$app = JFactory::getApplication('site');

// Include dependancies
require_once '../controller.php';

// This is specific to this component
define('JPATH_COMPONENT', JPATH_BASE . "/components/com_abc");

class controllerTest extends PHPUnit_Framework_TestCase
{
    public function testCronjob()
    {

        // Need access to the controller
        $controller = JController::getInstance('Abc');

        // Should return
        $this->assertEquals('test', $controller->execute("cronjob"));

    }
...

Ideally someone could direct me to an open source Joomla component that has tests & instructions on how to run them (had a quick look, first 5 or so didn't have tests).

I found it easy enough to roll my own. I didn't even bother to set up phpunit.xml.

  • I finally got around to trying this out (haven't used Joomla in a long time now!). Seems to do the trick! – uozuAho Sep 1 '15 at 23:57
  • I get Error: Call to undefined method JController::getInstance() for this. – Olle Härstedt Feb 14 '17 at 16:21
6

Sorry if my answer does not directly addresses your issue, but it is important to understand what unit testing is and how to use it, no matter if we are talking about Joomla or not. You have addressed a lot of issues and it's rather hard to deal with all of them.

First of all it is important to read the documentation at PHPUnit. Build yourself a small class (independently of Joomla) and try to write some tests for it. Get PHPUnit first running in a such scenario. Understand what it does. I am afraid you are focusing too much on getting PHPUnit to run instead of understanding how it helps your development.

To get Joomla to run, you will need:

  • bootstrap.php - which will give you an instance of Joomla.
  • phpunit.xml - fine tuning PHPUnit settings. Useful for example of specifying once for a whole project where are the test folders in each component. So that you can run a suite of tests.

The error that you are getting is as clear as possible. 'ContentController' is clearly not autoloaded by Joomla. Check with a debugger if the autoloader in Joomla is called and why it fails to load the class. Worst case scenario provide your own autoloader and use the bootstrap file to call it. A quick fix is also manually loading the required file with require_once.

Decide what you want to test and use mockups

In the example provided you are using JModel to fetch data from the database. This is a no-go from the TDD point of view. Testing that you can fetch data from the database has no value IMHO. What exactly are you testing there?

If you need to work with data from the database, use mocks to model a response from the database. This way your tests will have a consistent behavior over time. You don't want to have failed tests just because you changed something in the database.

If you have a method that computes somethings or anything else that have sense to test. This is unit testing: you take a small unit from your complex system, feed it with fake test data and test it's behaviour.

Need to see that your system works as a whole, use system tests.

  • Sorry but this answer is not very relevant. I've used PHPUnit before, and understand the point of testing. I'm just struggling to get my tests to even run. The test in my question is not important, the important part is that it runs. I've edited my question in the hope of making this clearer. – uozuAho Sep 25 '14 at 2:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.