Features

Running CakePHP2 Unit Tests in PhpStorm

CakePHP2 comes with unit testing support powered by PHPUnit. In addition to the features offered by PHPUnit, CakePHP offers some additional features to make testing easier. Unit tests in CakePHP rely on a custom test runner which can be run in the browser or using the CakePHP console. More information on unit testing with CakePHP can be found on their website.

One issue with CakePHP unit tests is that they rely on the CakePHP test runner. This means that running them directly from PhpStorm is not supported out of the box. Which does not mean it’s impossible! Let’s see if we can get CakePHP unit tests running from PhpStorm.

Understanding how PhpStorm runs unit tests

Before we can make CakePHP unit tests run in PhpStorm, let’s find out how PhpStorm runs unit tests in the first place. A good way to discover this is by creating a simple project in PhpStorm and adding a unit test run configuration to it. The unit test tab shows us what is happening under the covers.

When scrolling a bit to the right, we can see PhpStorm is invoking a script named ide-phpunit.php which it stores in a temporary location on our system. What’s interesting is that this file is not deleted immediately after the test run, so it can be opened just like any other PHP file. Let’s have a look inside.

The script ide-phpunit.php is a simple wrapper around multiple versions of PHPUnit, returning unit test results to the IDE. It contains several classes, like IDE_PHPUnit_TextUI_Command (extending PHPUnit_TextUI_Command) which is a custom PHPUnit test runner. Since CakePHP uses a similar approach for bootstrapping their unit tests, we may be on to something!

Some of the most interesting lines of code in this file are the following:

What happens here is that PHPUnit is configured in such way that it can report test results to PhpStorm through a custom test result listener and a custom test result printer.

If we scroll to the bottom of this file, we can see the following statement:

IDE_PHPUnit_TextUI_Command::main();

This command kicks off the actual test run. Based on these pointers, let’s craft a custom test runner for PhpStorm which bootstraps CakePHP unit tests!

Creating a CakePHP test runner for PhpStorm

Let’s start by creating a new test runner called IDE_Cake_PHPUnit_TextUI_Command and inheriting from CakePHP’s test runner, CakeTestSuiteCommand. This class will implement two functions: main(), which starts the test runner, and the handleArguments() function which intercepts PHPUnit’s configuration arguments.

Using some custom argument parsing in the main() function, we can provide CakePHP’s test runner with some additional parameters which are not supported by PHPUnit directly. The loop over the argument values being passed in strips out any command-line switch which starts with –cake- and passes it to CakePHP’s test runner as custom options. Using this approach we can later use a –cake-case=AllTests command-line switch to instruct CakePHP which tests to run.

The handleArguments() function is a blatant copy-paste from the ide-phpunit.php script. It configures PHPUnit to report test results back to PhpStorm using a custom test result listener.

We can save this file in the root of our project and name it cakeunit4phpstorm.php. If you’re interested in the complete source code already, it’s available from here.

Tricking PhpStorm into using a custom test runner

We’re not there yet. There is one major issue we can not solve: PhpStorm always creates a fresh copy of ide-phpunit.php. Hence we can not add our custom test runner code nor invoke it from that script. Hrm, now what?…

Fortunately, test run configurations in PhpStorm provide us with an option to specify PHP interpreter arguments. And PHP has this lovely -f switch which allows us to provide a custom script to run…

If we now run our unit tests, the custom test runner we’ve created is invoked but it’s not yielding the results we expected from it… The reason for that is we’ve removed every association with the ide-phpunit.php script and classes therein. Back to the drawing board.

In the cakeunit4phpstorm.php script, let’s apply a small trick to the arguments being passed from PhpStorm.

The first loop we’re doing is intended to “clean” argument values. We’re interested in keeping all command-line arguments except if it is the path to ide-phpunit.php. Why not, you ask? Well, PHP will get confused if we pass it two scripts to run, hence we’re removing PhpStorm’s generated test runner from the command-line arguments. Note we do store the path to ide-phpunit.php as we want to include it later on.

Before including the original PhpStorm test runner, we want to make sure it’s not invoking itself. The last line of it was IDE_PHPUnit_TextUI_Command::main(), we want to be sure that line is not included by simply commenting it out.

Finally, we include ide-phpunit.php and all classes in there to make use of what is already in the box: a means to communicate test results with PhpStorm.

Using the CakePHP test runner with PhpStorm

Let’s put our new test runner to the test. If you haven’t done so yet, add cakeunit4phpstorm.php to your project root. Its sources are available from https://gist.github.com/4529548.

Next, create a test run configuration for your CakePHP project. Make sure to specify test runner options according to the arguments you would normally use with ./Console/Cake test. These arguments should be in the format –cake{var}={value}.

Some examples:

●     Run application test named “Sample”: –cake-case=Sample –cake-app

●     Run core test named “AllTests”: –cake-case=AllTests –cake-core

Finally, make sure the PHP interpreter options are specified. The value for the -f switch should be the full path to cakeunit4phpstorm.php.

Here’s an example configuration for running all core tests:

We can now run this test configuration and get results from CakePHP unit tests in PhpStorm. We can inspect CakePHP test results right from our IDE.

Enjoy CakePHP with PhpStorm! Step-by-step tutorial is available here.

Develop with pleasure!
– JetBrains Web IDE Team