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.
Might be an interesting addition to LiipFunctionalTestBundle
man the next post is the killer one, thanks a lot!
besides being silly testing on prod code, I wonder if rather the application can become for use cases where one needs to monitor processes and examine code right on the server. Hmm I am trying to think of good apps.