by finne on 28-01-2017

Debugging tests in Drupal 8

If you are working on tests in Drupal 8 you will want to debug your code just like you debug normal application code. However tests are run by the PHPUnit testrunner, and this makes setting up a debug configuration quite complicated.

Here I will explain the setup that I use for debugging UnitTest based (PHPUnit_Framework_TestCase class) Drupal 8 tests. I will configure a local development environment using PhpStorm 2016.3 and MAMP PRO 4 on macOS Sierra. The configuration should also work if you use other local tools on a Mac/Linux OS instead of MAMP.

NB: older Drupal code still uses Simpletest as base (TestBase class) for its tests. This article does not cover Simpletest based tests.

To start off you need a Drupal 8 installation. Do a standard install using your preferred method. (Drupal Console chain, git checkout, etc).

Enable Debugging

First enable debugging in your php.ini.

In MAMP PRO got to PHP > Extentions and enable Xdebug.
Otherwise locate your local webserver's php.ini and add the following:

zend_extension="<path to xdebug.so>"
xdebug.remote_enable=1
xdebug.remote_host=localhost
xdebug.remote_port=9000
xdebug.remote_autostart=1

Sanity check#1: Now if you enable debug listening in PhpStorm and set a breakpoint in index.php and reload your local Drupal installation in your browser your debugger should halt at the breakpoint in PhpStorm.

Next enable command line (CLI) debugging.
In MAMP PRO 4 got to PHP > Default version and enable "Make this version available on the command line".
Otherwise you will need to make sure the CLI php.ini (there usually is a separate CLI version) has the debug config info set. I recommend to use the same php version and same php.ini on the CLI that you use in your webserver, so you only need to set your config in one place.

Check by executing 

php -i|grep xdebug

This should report your ini settings above. If not, make sure your php command uses the correct php executable, set the correct path variable in your shell if needed:

export PATH=/Applications/MAMP/bin/php/php7.1.0/bin:$PATH

Sanity check #2: Now if you enable debug listening in PhpStorm and run a Drush command (drush status) your debugger should halt at the first line of the drush php script file in PhpStorm.

NB: some Functional Tests fail when you run drupal from a subdir. Always make sure your test installation is reachable at the docroot: http://example.dev instead of http://example.dev/subdir.

configure PhpStorm

Debugging tests using Xdebug is different from debugging other processes, because tests go through cURL rather than through your browser. The second PHP process started by the cURL request should be passed the correct session variables, get parameters, and cookies so that it will connect to the Xdebug client and the Xdebug client should accept multiple connections.

To enable all this in PhpStorm set the following config:

  • Preferences > Languages & Frameworks > PHP
    • PHP language level: match this to the version of PHP you use on the CLI.
    • CLI Interpreter: set the interpreter to the same php executable as the CLI. Click the … button to add an interpreter or to check existing interpreter supports Xdebug. It should report a PHP version (7.1.0) and an Xdebugger version (2.5.0) and the correct ini file path.
  • Preferences > Languages & Frameworks > PHP > Debug
    • External connections: set the Max. simultaneous connections to at least 5.
    • Disable Force break when no path mapping specified.
    • Disable Force break when no outside the project.
  • Preferences > Languages & Frameworks > PHP > PHPUnit
    • PHPUnit library: Use Composer autoloader: Set 'Path to script' to the Drupal root autoloader: "/Applications/MAMP/htdocs/d8development/autoload.php" in my case.
    • Test Runner: Set the default configuration file to your core/phpunit.xml file (It might not yet exist). "/Applications/MAMP/htdocs/d8development/core/phpunit.xml" in my case.

Optionally: When you use MAMP PRO 4 the php CLI environment is configured using the .profile shell script. This sets up a $PHPRC environment variable to point to the php.ini. Since $PHPRC is set during bash start-up, it's never set for PhpStorm. If you don't see the correct php.ini in the CLI Interpreter settings you can force PhpStorm to use your shell environment: Go to Tools > Create Command-line Launcher... and save a CLI launcher for PhpStorm. Now quit PhpStorm and open your Terminal and invoke the command-line PhpStorm launcher. PhpStorm will now launch from a configured shell, and your php.ini should be correct in the CLI Interpreter settings.

Configure PHPUnit

Copy the PHPUnit config file core/phpunit.xml.dist to core/phpunit.xml. Out of the box UnitTests should work. More advanced tests need some extra configuration, and you can also tweak the settings in other ways.

  • For more advanced tests (KernelTest and up) configure a database path:
    edit the <php> tag: add <env name="SIMPLETEST_DB" value="sqlite://localhost//Applications/MAMP/htdocs/d8development/sites/default/files/test.sqlite"/>
  • For more even more advanced tests (Functional and up) configure a base URL: 
    edit the <php> tag: add <env name="SIMPLETEST_BASE_URL" value="http://localhost/d8development"/>
  • To allow output during a test script (so you can use print, echo and var_dump):
    edit the <phpunit> tag: set beStrictAboutOutputDuringTests="false"
  • To print the PHP and Xdebug version and config file path:
    edit the <phpunit> tag: set verbose="true"
  • To show more progress during testing:
    In PhpStorm the current progress and test being run are shown on the left in the run panel.
    On the CLI run with the —debug flag: vendor/bin/phpunit --debug --configuration core/phpunit.xml <path to test class file>
  • To save the testing results:
    Define a logger in phpunit.xml:
    <logging>
      <log type="testdox-text" target="/Applications/MAMP/htdocs/d8development/sites/default/files/testdox.txt"/>
    </logging>
    

    This gives you a file with a list of all tests that ran, and whether or not they failed.

  • To enable automatic HTML screenshots when running Functional tests:
    edit the <phpunit> tag: add printerClass="\Drupal\Tests\Listeners\HtmlOutputPrinter". NB: because of a bug this currently does not work. Use the CLI flag --printer="\Drupal\Tests\Listeners\HtmlOutputPrinter".
    edit the <php> tag: add <env name="BROWSERTEST_OUTPUT_DIRECTORY" value="/Applications/MAMP/htdocs/d8development/sites/default/files/browser_output"/>
    NB: this dir must exist before running tests.

Running tests

To debug tests run them with the PhpStorm debug listener on (even if you run a debug from PHPstorm - direct debug does not always work with breakpoints). To run a test in PhpStorm right click test function and choose run. To run from the CLI: 

vendor/bin/phpunit --debug --configuration core/phpunit.xml <path to test class file>

The 4 levels of tests are:

  1. Unit (tests based on the UnitTestCase class)
    example: core/modules/toolbar/tests/src/Unit/PageCache/AllowToolbarPathTest.php
  2. Kernel (tests based on the KernelTestBase class)
    set SIMPLETEST_DB in phpunit.xml
    example: core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php
  3. Functional (tests based on the BrowserTestBase class)
    set SIMPLETEST_BASE_URL in phpunit.xml
    example: core/tests/Drupal/FunctionalTests/Breadcrumb/Breadcrumb404Test.php
    PS: this test can auto generate HTML output screenshots after each drupalGet() test step. This only works when you initiate the test from the command line.
  4. FunctionalJavascript (tests based on the JavascriptTestBase class)
    start phantomjs on the CLI in the drupal root: phantomjs --ssl-protocol=any --ignore-ssl-errors=true --cookies-file=/tmp/cookies.txt --debug=true vendor/jcalderonzumba/gastonjs/src/Client/main.js 8510 1024 768
    example: core/modules/views/tests/src/FunctionalJavascript/ClickSortingAJAXTest.php

When you run a test from the contextual menu in PhpStorm a run configuration is saved for later re-use.

about the author

Finne Fortuin is Drupal development and deployment configuration expert.

Finne