How to unit test a Joomla 2.5 component - php

Can someone provide an example of how to unit test a Joomla 2.5 component? I'm working through this joomla mvc component example, which doesn't include unit tests, and I can't find a full example anywhere. My main questions are:
Where to put component test code
How to run component unit tests
Is my test code correct
The Joomla documentation seems incomplete and fragmented - I've read what I can find on unit testing (can't post links due to low rep), which seem to be about testing Joomla itself rather than extensions. 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
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
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>

Unit tests for Joomla are typically written to use the PHP Unit framework
To answer your questions
Where to put component test code: at the top level of your repo as follows
Component repo directory structure:
src
admin/
site/
tests
How to run component unit tests: test execution follows this pattern
vendor/bin/phpunit --configuration phpunit.xml
additional details can be found in the php unit documentation https://phpunit.de/documentation.html
Is your test code correct: no
You failed to mock your class and methods with the getMockBuilder() for testing.
Here are some examples to look at
https://github.com/joomla-framework/database
https://github.com/joomla-extensions/weblinks

Related

PHPUnit can't find a class loaded in the bootstrap

My problem with PHPUnit is quite simple: if the test class file is included in the bootstrap file, PHPUnit won't be able to find the test class.
You can easily recreate the issue:
The file hierarchy:
./tests/MyTest.php
./bootstrap.php
./phpunit.xml
phpunit.xml
<phpunit bootstrap="./bootstrap.php">
<testsuites>
<testsuite name="MyTest">
<directory>./tests</directory>
</testsuite>
</testsuites>
</phpunit>
bootstrap.php:
<?php
require __DIR__ .'/vendor/autoload.php';
require __DIR__ .'/tests/MyTest.php';
tests/MyTest.php
<?php
namespace App\Tests;
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
public function testTrue()
{
$this->assertTrue(true);
}
}
In this case, when I run ./vendor/bin/phpunit, it returns "No tests executed!". And if I run ./vendor/bin/phpunit ./tests/MyTest.php, it returns "Class 'MyTest' could not be found in '[...]\tests\MyTest.php'.".
Now, if I remove the second require_once from autoload.php, both commands work as expected.
I've only seen a couple of similar issues on stack. They are six years old and have not been resolved, which makes me think that not allowing this way of loading classes is actually a design choice.
Could you please help me? Thank you!

PHPUnit test error, cannot find class

I'm new in PHPUnit and unit-testing, so I was install PHPUnit and phar via composer and everything had been going fine until I was try to start my simple test. I'm using PhpStorm where I can see all classes were autoload, but when I trying to start my test I got an error:
Fatal error: Class 'PharIo\Manifest\Simple' not found in C:\xampp\htdocs\mydocs\
I don't understand why he is looking for It in folder upper than PHPUnit is exists ?
I was trying to configure autoload section in composer.json and checking settings in phpunit.xml but nothing works.
Add:
I have to reinstall PHPUnit without PharIO, so now I have a little bit of progress, now I have a situation where I can test my class if I make require_once line with a name of the tested class. It looks like:
require_once '../src/Simple.php';
class SimpleTest extends PHPUnit_Framework_TestCase
{
public function testAdd() {
$sum = new Simple();
$this->assertEquals(5, $sum->add(2, 3));
}
}
So my simple class is:
class Simple {
public function add($a, $b) {
return (int) $a + (int) $b;
}
}
But, of course, I want to use namespaces. I try to make some changes based on this question: Autoloading classes in PHPUnit using Composer and autoload.php (I was try even use that repo for test, but an error is still exists) but nothing works for me. I was try to edit my autoload section in the composer.json like this
"autoload": {
"psr-4": {
"app\\": "src/"
}
},
But an error is still exists, another words autoload cannot see It. I was create phpunit.xml and phpunit.dist.xml with a same settings
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/3.7/phpunit.xsd"
backupGlobals="true"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./tests/bootstrap.php">
<testsuites>
<testsuite name="The project's test suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
</phpunit>
and I made tests/bootstrap.php too with
require_once '../vendor/autoload.php';
I know this is an old question, but maybe you need to do
composer dump-autoload for composer to generate the map of classes.
I wasted 30mins trying to understand why PHPUnit was giving me:
Cannot stub or mock class or interface XXX because it doesn't exists
You should specify the script with autoloading classes.
You can either specify the file with autoloading in XML-file, as suggested in the other answer, or just by specifying --bootstrap option in your command to run tests:
phpunit --bootstrap vendor/autoload.php tests
Composer's autoload relies on configuration located in the vendor/autoload.php file which needs to be loaded at some point in your execution thread. You application already includes this and that's why it works, but the tests use a different entry point so you need to configure it with a file called phpunit.xml.dist.
Assuming your file structure is something like:
app/
src/
tests/
bootstrap.php <- create it in your test folder
vendor/
...
composer.json
composer.lock
phpunit.xml.dist <- create it if does not exist
You can see the various options here, but for a basic config, you can use this.
File phpunit.xml.dist:
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/3.7/phpunit.xsd"
backupGlobals="true"
backupStaticAttributes="false"
bootstrap="tests/bootstrap.php">
</phpunit>
File tests/bootstrap.php:
require_once '../vendor/autoload.php';
You should run phpunit from the root.

PHPUnit in a legacy environment

I'm starting to setup PHPUnit (v4.8) to be used in my 'legacy' code (it's not so legacy, but it have bad programming practices).
The structure of my folders is as follows
/home
-phpunit.xml
/folder1
/folder2
/folder3
/vendor
/tests
-Test1.php
/includes
-functions.php
/libs
-User.php
-TableClass.php
....
functions.php
<?php
//require_once $_SERVER['DOCUMENT_ROOT'] . "/home/vendor/autoload.php" ;
require_once $_SERVER['DOCUMENT_ROOT'] . "/home/includes/libs/table_classes/User.php" ;
?>
I have commented that line, because I think composer automatically loads it. Question 1, Am I Rigth? (because phpunit get automatically recognized inside my Test class...)
Test1.php
<?php
class Test1 extends PHPUnit_Framework_TestCase
{
public function testSomething()
{
// $something = getColNameByStatusId(1);
$this->assertEquals(1,2);
}
}
?>
phpunit.xml
<phpunit bootstrap="includes/functions.php" colors="true">
<testsuite name="Test1" >
<directory>./tests</directory>
</testsuite>
</phpunit>
Then I Execute phpunit in command line
My functions.php works fine in my code, of course with no composer integration, but when It's loaded with phpunit it 'breaks', I get the following error:
Warning: require_once(/home/includes/libs/table_classes/User.php): failed to open stream: No such file or directory in C:\wamp\www\home\includes\functions.php on line 18
I think I'm missing the 'loading' stuff for phpunit. My code doesn't use namespaces and PSR-0 neither PSR-4.
Question 2- How to properly load files in this case?
My goal is to load functions.php then it will load all other 'table' classes for doing my tests
Replace $_SERVER['DOCUMENT_ROOT'] with __DIR__ and adjusted the paths accordingly, and everything worked fine.
PHPUnit does not set $_SERVER['DOCUMENT_ROOT'] so It was not finding my files. Apache's do that. So the CLI of PHPUnit couldn't find it.
Hope it helps someone else.
I think it is better to start using PHPUnit by running
phpunit --generate-configuration
and follow some simple questions.
To autoload 'functions.php' and other table 'classes', you may try via composer.json autoload.
"autoload": {
"psr-4": {
"Model\\": "libs/"
}
}
Here is the link with autoload for your reference.

PHPUnit & Zend Framework

I'm new to unit tests, i have been working on this tutorial i found on internet :
http://blog.fedecarg.com/2008/12/27/testing-zend-framework-controllers/
My problem is i simply can't execute the tests displayed in the tutorial!
C
:\wamp\www\portailmg\dev\tests>phpunit PHPUnit 3.7.21 by Sebastian
Bergmann.
Configuration read from C:\wamp\www\portailmg\dev\tests\phpunit.xml
Time: 0 seconds, Memory: 4.00Mb
No tests executed!
Generating code coverage report in HTML format ... done
C:\wamp\www\portailmg\dev\tests>
My bootstrap.php which is the only file i edited because i had the following error :
Notice: Zend_Loader::Zend_Loader::registerAutoload is deprecated as of
1.8.0 and will be removed with 2.0.0; use Zend_Loader_Autoloader instead in /www/zf-tutorial/library/Zend/Loader.php
I tried to fix this with that :
This is because you have the lines:
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();
(or similar) somewhere in your bootstrap system.
The easiest solution is to change them to:
require_once 'Zend/Loader/Autoloader.php';
$loader = Zend_Loader_Autoloader::getInstance();
$loader->registerNamespace('App_');
Where 'App_' is the name of a directory on your include path that has classes within it that follow the Zend Framework naming convention, so change it as appropriate and add more if you need them.
My bootstrap:
<?php
error_reporting( E_ALL | E_STRICT );
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
date_default_timezone_set('Europe/London');
define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../applications'));
define('APPLICATION_ENV', 'loc');
define('LIBRARY_PATH', realpath(dirname(__FILE__) . '/../library'));
define('TESTS_PATH', realpath(dirname(__FILE__)));
$_SERVER['SERVER_NAME'] = 'http://localhost';
$includePaths = array(LIBRARY_PATH, get_include_path());
set_include_path(implode(PATH_SEPARATOR, $includePaths));
require_once 'Zend/Loader/Autoloader.php';
$loader = Zend_Loader_Autoloader::getInstance();
$loader->registerNamespace('LIBRARY_PATH');
Zend_Session::$_unitTestEnabled = true;
Zend_Session::start();
?>
Thanks in advance for your help
Your setup looks okay, although if I remember correctly zend framework 1 will only work with >=3.5.x so maybe downgrading from 3.7 to 3.5 may do the trick. Do ensure your phpunit.xml file is setup properly and pointing to the tests bootstrap and not your application bootstrap. Also be sure to be following the unit testing name conventions. See http://phpunit.de/manual/3.5/en/index.html

Setup PHPUnit with Zend Test

I'm trying to start using PHPUnit with Zend Test for my Zend Framework application. I'm able to run the PHPUnit command from command line phpunit --configuration phpunit.xml. I've tried following this tutorial which is based off of Matthew Weier O'Phinney's blog post. I'm getting an error when PHPUnit tries to write the log file. Here's my phpunit.xml
<phpunit bootstrap="./Bootstrap.php" colors="true">
<testsuite name="Zend Framework Tests">
<directory>./</directory>
</testsuite>
<!-- Optional filtering and logging settings -->
<filter>
<whitelist>
<directory suffix=".php">../library/</directory>
<directory suffix=".php">../application/</directory>
<exclude>
<directory suffix=".phtml">../application/</directory>
</exclude>
</whitelist>
</filter>
<logging>
<log type="coverage-html" target="./log/report" charset="UTF-8" yui="true" highlight="true" lowUpperBound="50" highLowerBound="80"/>
<log type="testdox-html" target="./log/testdox.html"/>
</logging>
</phpunit>
My testing bootstrap:
<?php
//Set app paths and environment
define('BASE_PATH', realpath(dirname(__FILE__) . '/../'));
define('APPLICATION_PATH', BASE_PATH . '/application');
define('TEST_PATH', BASE_PATH . '/tests');
define('APPLICATION_ENV', 'testing');
//Set include path
set_include_path('.' . PATH_SEPARATOR . BASE_PATH . '/library' . PATH_SEPARATOR . get_include_path());
//Set the default timezone
date_default_timezone_set('America/Chicago');
?>
And my ControllerTestCase that I would like my testing controllers to extend:
<?php
require_once 'Zend/Application.php';
require_once 'Zend/Test/PHPUnit/ControllerTestCase.php';
abstract class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
public $_application;
public function setUp()
{
//Override the parent to solve an issue with not finding the correct module
$this->bootstrap = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
parent::setUp();
}
}
?>
The error I get when PHPUnit tries to write the log file is:
Fatal error: Class 'Symfony\Component\Console\Command\Command' not found in C:\repositories\myfirstzend.com\includes\library\Doctrine\DBAL\Tools\Console\Command\ImportCommand.php on line 38
Any clues on what I'm doing wrong? I'm on PHP 5.4, Windows 7, XAMPP 8.0, Pear is up to date, and I have the latest PHPUnit.
Update If I change my Bootstrap.php to the following from Matthew Weier O'Phinney's blog:
<?php
/*
* Start output buffering
*/
ob_start();
/*
* Set error reporting to the level to which code must comply.
*/
error_reporting( E_ALL | E_STRICT );
/*
* Set default timezone
*/
date_default_timezone_set('GMT');
/*
* Testing environment
*/
define('APPLICATION_ENV', 'testing');
/*
* Determine the root, library, tests, and models directories
*/
$root = realpath(dirname(__FILE__) . '/../');
$library = $root . '/library';
$tests = $root . '/tests';
$models = $root . '/application/models';
$controllers = $root . '/application/controllers';
/*
* Prepend the library/, tests/, and models/ directories to the
* include_path. This allows the tests to run out of the box.
*/
$path = array(
$models,
$library,
$tests,
get_include_path()
);
set_include_path(implode(PATH_SEPARATOR, $path));
/**
* Register autoloader
*/
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();
/**
* Store application root in registry
*/
Zend_Registry::set('testRoot', $root);
Zend_Registry::set('testBootstrap', $root . '/application/bootstrap.php');
/*
* Unset global variables that are no longer needed.
*/
unset($root, $library, $models, $controllers, $tests, $path);
I continue to get the error about Symfony from Doctrine. I ensured that I installed pear.symfony.com/Yaml as well. So still broken.
If I remove the Doctrine reference from my app.ini for the application I'm testing (which means it doesn't get loaded), I still get the error. It almost feels like the loaders for each of the three parts (PHPUnit, ZF, Doctrine) are fighting each other. Is there a way around this?
Second update: I downgraded PHPUnit to 3.4.15 and I'm still having this issue. My next step is to go from PHP 5.4 to 5.3.x.
Third update: I am now on PHP 5.3.10 and am seeing the same error.
If there's more information you need, please let me know.
I'm missing the loading of the application in your bootstrap.
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
To give you an idea about what I have in my tests/bootstrap.php (this is auto-generated by zend tool since release-1.11.4)
<?php
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'testing'));
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
As mentioned by the Zend Framework manual, best is to use PHPUnit 3.4.15 although I could run my tests using PHPUnit 3.6.12 as I'm isolating my tests to focus on my business logic and not the logic of Zend Framework.
I also modified my phpunit.xml as well into the following:
<phpunit bootstrap="./bootstrap.php" colors="true">
<testsuite name="Application Test Suite">
<directory>./application</directory>
</testsuite>
<testsuite name="Library Test Suite">
<directory>./library</directory>
</testsuite>
<filter>
<whitelist>
<directory suffix=".php">../../library</directory>
<directory suffix=".php">../../application</directory>
<exclude>
<directory suffix=".php">../../library/Zend</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
I hope this solves many of your issues you're facing now.
Best regards,
Michelangelo

Categories