Symfony2: running PHPUnit from within a controller

Posted on by Matthias Noback

This article is deprecated. I don't think it's a good idea to run your unit tests in a controller.

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.

PHP Testing Symfony2 PHPUnit controller
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).
Lukas

Might be an interesting addition to LiipFunctionalTestBundle

cordoval

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.