-
Notifications
You must be signed in to change notification settings - Fork 7
Unit Tests
Repoman (and Composer) were designed to help you product testable code. The most popular tool for writing PHP unit tests is PHPUnit. It's a command line tool -- sometimes its documentation is lacking, but hopefully the sample below will get you started. Another tool that is available is called SimpleTest, but PHPUnit is the standard. Beginners may find SimpleTest easier to work with because its documentation is clearer.
- When we say "tests", we are referring to functions in a test class. The purpose of these functions are to test things.
- PHPUnit is a command-line tool: you do not execute its tests from a browser. (You can use SimpleTest for that).
- Writing testable code is a really good habit to get into, but it takes practice and experience to do well. Try to write your classes and functions so they do one thing, and write them so you can pass in the information they need as arguments (either as arguments to the function or arguments to the class constructor). That makes it easier to test.
Save the following code as tests/myTest.php
. PHPUnit will look for a special function name before running the test functions in the class: setUpBeforeClass. We can use that function to instantiate an instance of MODX and store it as a class variable for our tests. The following should be a useful template for creating test classes for your projects.
<?php
/**
*
* To run these tests, pass the test directory as the 1st argument to phpunit:
*
* phpunit path/to/tests
*
* or if you're having any trouble install phpunit, download its .phar file, and
* then run the tests like this:
*
* php phpunit.phar path/to/tests
*
* To limit the tests to only one set of tests, you can specify a single file:
*
* phpunit tests/myTest.php
*
* All classes you create in this directory
*/
class myTest extends PHPUnit_Framework_TestCase {
// Must be static because we set it up inside a static function
public static $modx;
/**
* Load up MODX for our tests.
*/
public static function setUpBeforeClass() {
$docroot = dirname(dirname(dirname(dirname(__FILE__))));
while (!file_exists($docroot.'/config.core.php')) {
if ($docroot == '/') {
die('Failed to locate config.core.php');
}
$docroot = dirname($docroot);
}
if (!file_exists($docroot.'/config.core.php')) {
die('Failed to locate config.core.php');
}
include_once $docroot . '/config.core.php';
if (!defined('MODX_API_MODE')) {
define('MODX_API_MODE', false);
}
include_once MODX_CORE_PATH . 'model/modx/modx.class.php';
self::$modx = new modX();
self::$modx->initialize('mgr');
// Include this if you are using autoloading:
if (file_exists(dirname(dirname(__FILE__)).'/vendor/autoload.php')) {
include_once dirname(dirname(__FILE__)).'/vendor/autoload.php';
}
}
/**
* Example Test
*/
public function testMODX() {
$this->assertTrue(defined('MODX_CORE_PATH'), 'MODX_CORE_PATH not defined.');
$this->assertTrue(defined('MODX_ASSETS_PATH'), 'MODX_ASSETS_PATH not defined.');
$this->assertTrue(is_a(self::$modx, 'modX'), 'Invalid modX instance.');
}
// Add more tests here, just begin your function names with "test":
}
If you wrote your with PHPUnit, you run them using the PHPUnit command line utility. Many developers install this globally on their development machines so they can simply type phpunit
to run the script. When you run the tests, you reference your test directory as an argument.
If you are in your project's root directory, you can run all of your tests like this:
phpunit tests/
If you had any trouble installing PHPUnit, then I recommend downloading the .phar file and running that in place of the phpunit
command. That looks like this:
php path/to/phpunit.phar tests/
You just have to keep your paths straight.
To run only the tests in a single testing class, just specify the file name:
phpunit tests/myTest.php
You can set common options in the phpunit.xml file and avoid having to type the options each time you run phpunit
from the command line. Here's an example:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Application Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
Note the bootstrap="tests/bootstrap.php"
: that file gets loaded when tests are run, so it's a great place to include the necessary MODX class files that you need to run your tests.
Because you don't know exactly where your repository will be installed, it's helpful if you have some code that will find the MODX classes for you regardless of where it is.
Here is some sample code for bootstrap.php that uses a while loop to traverse up the directory tree until it finds a MODX config file:
<?php
require_once dirname(dirname(__FILE__)).'/vendor/autoload.php';
// Find MODX
$docroot = dirname(dirname(dirname(dirname(__FILE__))));
while (!file_exists($docroot.'/config.core.php')) {
if ($docroot == '/') {
die('Failed to locate config.core.php');
}
$docroot = dirname($docroot);
}
if (!file_exists($docroot.'/config.core.php')) {
die('Failed to locate config.core.php');
}
include_once $docroot . '/config.core.php';
if (!defined('MODX_API_MODE')) {
define('MODX_API_MODE', true);
}
include_once MODX_CORE_PATH . 'model/modx/modx.class.php';
/*EOF*/
A sample test class for PHPUnit might look something like this:
<?php
class snippetTest extends \PHPUnit_Framework_TestCase {
// Must be static because we set it up inside a static function
public static $modx;
/**
* Load up MODX for our tests.
*/
public static function setUpBeforeClass() {
self::$modx = new \modX();
self::$modx->initialize('mgr');
}
/**
* Test mySnippet
*/
public function testMySnippet() {
// You MUST set $modx as a global variable, or runSnippet will encounter errors!
// You have to do this for EACH test function when you are testing a Snippet!
global $modx;
$modx = self::$modx;
$props = array();
$props['x'] = 'something';
$actual = self::$modx->runSnippet('mySnippet', $props);
$expected = 'Some output here';
$this->assertEquals($expected, $actual);
}
WARNING: When running unit tests that must test Snippet output, you must keep your environment in mind. MAMP in particular may fail if you run the tests as a non-admin user because the admin user is required to write cache files.
Output Filters are Snippets, so you can test them in the same way, just remember
public function testSecure() {
global $modx;
$modx = self::$modx;
$props = array();
$props['options'] = 'something';
$props['name'] = 'price';
$props['input'] = '29';
$actual = $modx->runSnippet('myoutputfilter', $props);
$expected = '$29.00';
$this->assertEquals($expected, $actual);
}
#Related Configuration
Repoman does not include your tests/
directory when your package is built. This is because Repoman lets you omit any file or directory from the build, and the "test" directory is one of the default directories that gets skipped. If you rename your tests directory or you want to omit other directories from the build, just update the "omit" array in your composer.json
"extra": {
"omit": ["mytests"]
}
© 2014 and beyond by Craftsman Coding