Symfony2: running PHPUnit from within a controller
Matthias Noback
When you don’t have access to the command-line of your webserver, it may be nice to still run all your unit tests; so you need a way to execute the phpunit
command from within a controller. This way, you can call your test suite by browsing to a URL of your site. To do things right, we start with a “test” controller /web/app_test.php
containing these lines of code:
if (!in_array(@$_SERVER['REMOTE_ADDR'], array(
'127.0.0.1',
'::1',
))) {
header('HTTP/1.0 403 Forbidden');
exit('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
}
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
use Symfony\Component\HttpFoundation\Request;
$kernel = new AppKernel('test', true);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();
In fact, just copy everything from /web/app_dev.php
but replace the string “dev” by “test”.
This will enable you to run the site in a “test” environment, using for example your “test” database connection and what not.
Then create a route for our “runTests” action. Do this in /app/config/routing_test.yml
, so the tests can only be run from the “test” environment. The new routing_test.yml
contains these routes:
_demo_run_tests:
pattern: /demo/run-tests/{filterClass}
defaults: { _controller: AcmeDemoBundle:Demo:runTests, filterClass: null }
_main:
resource: routing.yml
The extra query parameter “filterClass” will allow us (in a few moments) to filter the tests to run, so we don’t have to run the complete test suite all the time.
Make sure routing_test.yml
gets loaded from config_test.yml
, by adding a “routing” option to the “framework” section of this file:
framework:
router: { resource: "%kernel.root_dir%/config/routing_test.yml" }
In the DemoController
of the AcmeDemoBundle
, we create the action “runTests”. In this action, we put the following code (for a start):
public function runTestsAction($filterClass = null)
{
// make sure PHPUnit is autoloaded
require_once('PHPUnit/Autoload.php');
set_time_limit(0); // make the script execution time unlimited (otherwise the request may time out)
// change the current directory to the place where phpunit.xml(.dist) can be found
chdir($this->container->getParameter('kernel.root_dir'));
ob_end_clean(); // cleans and ends existing output buffering
echo '<pre>';
// simulate an array of command line arguments
$argv = array();
if ($filterClass !== null) {
array_push($argv, '--filter', $filterClass);
}
$_SERVER['argv'] = $argv;
\PHPUnit_TextUI_Command::main(false); // true means: exit
echo '</pre>';
exit;
}
This will result in good old PHPUnit to run inside your browser (see it for yourself at /app_test.php/demo/run-tests
)! It is all quite basic (actually: not basic enough, since by default color coding is turned “on”, which results in ugly characters all over your code). So in my next post, I will show you how to make things a bit better for HTML output, by creating a custom ResultPrinter
. In this post, we will also look into the problem of output buffering; calling PHPUnit from within the controller means your output will be buffered by default, so you won’t see any progress, until all tests are done.