PHPUnit in PhpStorm: Run Configurations, Coverage and Test Filters
AI generated
@test
assert
PHPUnit · PhpStorm · Coverage · Docker
PHPUnit in PhpStorm
Run Configurations, Coverage and Test Filters

Anyone who only runs PHPUnit from the command line is giving up most of PhpStorm's productivity. Run Configurations, coverage reports right in the editor, test filters down to a single method, and the Docker remote interpreter turn PhpStorm into a complete testing environment, without ever leaving the terminal behind for good reason.

15 min read Run Config · Coverage · Xdebug · PCOV · Docker PHPUnit 10/11 · PhpStorm 2024 · PHP 8.3/8.4

1. Why PhpStorm integration changes the testing workflow

The typical picture in many PHP teams looks like this: tests run in a terminal, output scrolls past, and error messages get copied back into the source code by hand. PhpStorm offers a fundamentally different approach. The test runner is embedded directly in the editor. A failed assertion opens the source file with one click, landing on the exact line that caused the problem. Coverage information is shown as colored line highlighting directly in the editor, with no need to open an HTML report.

The decisive step toward this integration is a properly configured PHPUnit Run Configuration in PhpStorm. This configuration determines which PHP interpreter is used, where phpunit.xml lives, which bootstrap file gets loaded, and how tests should be filtered. For Magento projects with a Docker setup, a remote interpreter inside the container is added on top, because PHP is not installed locally at all; it only runs inside the Docker container.

2. Remote interpreter: the Docker container as the PHP source

The first step toward a working PHPUnit integration in PhpStorm is setting up a remote interpreter. For projects using the Mark Shust Docker setup, that means PhpStorm connects to the running PHP container via the Docker SDK and executes PHP processes directly inside it. The result is test execution and coverage analysis that behave identically to the container itself, without path problems caused by mismatched PHP versions or extensions.

Setup happens under Settings → PHP → CLI Interpreter → + → From Docker, Vagrant, VM.... As the server, you pick the Docker socket; as the image, the project's PHP container image. PhpStorm automatically detects the PHP version, installed extensions (Xdebug, PCOV), and Composer packages. Path mappings, the local project path mapped to the path inside the container, are essential: without a correct mapping, tests cannot find their dependencies and coverage information cannot be traced back to local files.


# docker-compose.yml: enable Xdebug for coverage (dev container only)
# PhpStorm uses this container as the remote interpreter

services:
  phpfpm:
    image: markoshust/magento-php:8.4-fpm
    environment:
      # XDEBUG_MODE=coverage enables only coverage, not debugging
      # This keeps the overhead minimal
      XDEBUG_MODE: "${XDEBUG_MODE:-off}"
    volumes:
      - ./src:/var/www/html
      # PhpStorm ships its own debug configuration here:
      - ./.phpstorm.helpers:/tmp/.phpstorm_helpers:ro
    extra_hosts:
      # PhpStorm host for Xdebug connections (Linux)
      - "host.docker.internal:host-gateway"

Important: XDEBUG_MODE=coverage enables only Xdebug's coverage collection, without starting the step debugger. That reduces performance overhead substantially compared to XDEBUG_MODE=debug,coverage. For a daily test run without coverage, XDEBUG_MODE=off can stay in place; PhpStorm can configure the Run Configuration so that Xdebug is only activated for an explicit coverage run.

3. Run Configurations: unit, integration, and suite kept separate

A single universal Run Configuration for all tests is counterproductive: it mixes unit and integration tests, runs unnecessarily long, and provides no fast feedback loop. The recommended practice is a hierarchy of Run Configurations: one for fast unit tests (runtime under 30 seconds), one for integration tests (with database access), one for the full test suite, and optionally one for a single test class during active development.

Run Configurations in PhpStorm are created under Run → Edit Configurations. Type: PHPUnit. The key fields are Test scope (Directory, File, Class, Method, or Suite from XML), PHP interpreter (the remote interpreter from the Docker container), phpunit.xml as the configuration source, and optional Environment variables for test database credentials. Run Configurations can be checked into version control as XML under .idea/runConfigurations/, so the whole team gets them immediately.


<?xml version="1.0" encoding="UTF-8"?>
<!-- .idea/runConfigurations/PHPUnit_Unit_Tests.xml -->
<!-- Check this file into git: team-wide configuration -->
<component name="ProjectRunConfigurationManager">
  <configuration default="false" name="Unit Tests" type="PHPUnitRunConfigurationType">
    <TestRunner location="vendor/bin/phpunit" />
    <option name="phpunit_ini" value="$PROJECT_DIR$/phpunit.xml" />
    <option name="scope_type" value="DIRECTORY" />
    <option name="directory" value="$PROJECT_DIR$/src/app/code" />
    <!-- Filter: unit tests only, no integration tests -->
    <option name="phpunit_arguments" value="--testsuite=Unit --no-coverage" />
    <option name="interpreter" value="docker-phpfpm" />
    <envs>
      <env name="XDEBUG_MODE" value="off" />
    </envs>
  </configuration>
</component>

4. Test filters: running individual tests, classes, and groups

The test filter in PHPUnit is the single most important tool for a fast development cycle. Instead of starting the entire test suite, you run exactly the test you are currently working on. In PhpStorm, a click on the green play button next to a test method or class is enough; PhpStorm automatically passes the correct --filter argument to PHPUnit. From the command line, the same result is achieved with --filter (a regular expression matched against method names) or --testsuite (a name defined in phpunit.xml).

Group-based filtering with the #[Group('slow')] attribute (PHPUnit 11) or @group slow annotation (PHPUnit 10) makes it possible to leave slow tests out of the fast development cycle and run them only during CI. The phpunit.xml file can exclude groups from standard suites or define separate suites for particular groups. That produces a multi-tier testing strategy: fast local tests under 10 seconds, and a full CI run covering every group.


<?php

declare(strict_types=1);

namespace Mironsoft\Catalog\Test\Unit\Model;

use Mironsoft\Catalog\Model\ProductEnricher;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

/**
 * Unit tests for ProductEnricher model.
 * Fast tests, no database, no external services.
 */
#[CoversClass(ProductEnricher::class)]
#[Group('unit')]
final class ProductEnricherTest extends TestCase
{
    /**
     * @test
     * PhpStorm shows green play button, click to run only this method.
     * CLI: vendor/bin/phpunit --filter testEnrichesSkuWithPrefix
     */
    #[Test]
    #[Group('fast')]
    public function testEnrichesSkuWithPrefix(): void
    {
        $enricher = new ProductEnricher(prefix: 'MRN-');
        $result = $enricher->enrich('ABC123');

        self::assertSame('MRN-ABC123', $result->getSku());
    }

    /**
     * @test
     * Slow test excluded from fast suite via Group attribute.
     * CLI: vendor/bin/phpunit --exclude-group slow
     */
    #[Test]
    #[Group('slow')]
    public function testProcessesLargeCatalog(): void
    {
        // simulate expensive operation
        self::assertTrue(true);
    }
}

5. Coverage: Xdebug vs. PCOV and reading results in the editor

Coverage reports in PhpStorm are shown directly in the source code: line highlighting shows at a glance which lines are covered by tests (green), which are not (red), and which never execute at all (gray). This works with two coverage drivers: Xdebug and PCOV. Xdebug is the well-known option with broad compatibility; PCOV is a lightweight driver focused exclusively on coverage, producing considerably less overhead.

In PhpStorm, coverage is started via Run → Run with Coverage or the shield button in the Run Configuration toolbar. PhpStorm then generates an HTML report, shows line coverage directly in the editor, and provides coverage statistics in the Coverage panel. For Magento projects it matters that the <source> list in phpunit.xml is scoped correctly, otherwise PhpStorm calculates coverage for vendor code, which makes the metrics meaningless and slows the run down.

6. phpunit.xml: preparing suites and filters for PhpStorm

The phpunit.xml file is the central configuration that PhpStorm points to for every Run Configuration. A well-structured XML file defines separate test suites, restricts the coverage source path, and sets environment variables for the test run. For Magento projects, it is worth maintaining two separate configurations: phpunit.xml for unit tests (fast, no database access) and phpunit-integration.xml for integration tests (with Magento bootstrap, slow, CI only).


<?xml version="1.0" encoding="UTF-8"?>
<!-- src/phpunit.xml: unit test configuration for PhpStorm -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
         stopOnFailure="false"
         cacheDirectory=".phpunit.cache">

  <testsuites>
    <testsuite name="Unit">
      <directory>app/code/Mironsoft/*/Test/Unit</directory>
    </testsuite>
    <testsuite name="Fast">
      <directory>app/code/Mironsoft/*/Test/Unit</directory>
      <exclude>app/code/Mironsoft/*/Test/Unit/Integration</exclude>
    </testsuite>
  </testsuites>

  <!-- Calculate coverage for our own code only -->
  <source>
    <include>
      <directory>app/code/Mironsoft</directory>
    </include>
    <exclude>
      <directory>app/code/Mironsoft/*/Test</directory>
      <directory>vendor</directory>
    </exclude>
  </source>

  <!-- Xdebug or PCOV as the coverage driver -->
  <coverage>
    <report>
      <html outputDirectory="var/coverage/html"/>
      <clover outputFile="var/coverage/clover.xml"/>
    </report>
  </coverage>

  <php>
    <env name="MAGE_MODE" value="test"/>
    <env name="DB_HOST" value="db"/>
    <env name="DB_NAME" value="magento_test"/>
  </php>
</phpunit>

7. Coverage drivers compared directly

The choice of coverage driver has a direct effect on how long the test suite takes to run. For a large codebase like Magento, the difference between Xdebug and PCOV can amount to several minutes. The following overview shows the practical differences.

Trait Xdebug (coverage mode) PCOV PHPUnit without coverage
Overhead Moderate (3 to 5x) Low (1.5 to 2x) None
Branch coverage Yes (complete) No (line coverage only) N/A
PhpStorm integration Full Full No coverage
Docker setup XDEBUG_MODE=coverage pecl install pcov No extension needed
Recommendation Full analysis, CI Fast local run TDD feedback loop

For the daily TDD cycle, the recommendation is: disable coverage completely (XDEBUG_MODE=off, no --coverage-* argument), which lets PHPUnit run at full speed. Coverage analysis is performed after a feature is finished, or in the CI run with PCOV. Xdebug with branch coverage is reserved for the full CI report. This three-tier split produces the fastest local feedback loop without giving up quality metrics.

Mironsoft

PHPUnit integration, coverage setup, and test architecture for PHP teams

Want PHPUnit and PhpStorm set up productively?

We set up remote interpreters, Run Configurations, and coverage drivers for your team, with Docker integration, team-shared configurations, and CI pipeline connectivity for Magento and PHP projects.

Setup & configuration

Set up remote interpreters, path mappings, and Run Configurations across the whole team

Coverage analysis

Configure Xdebug and PCOV, integrate reports into the CI pipeline

Team onboarding

Version Run Configurations and document the testing workflow for new developers

8. Summary

Fully integrating PHPUnit into PhpStorm comes down to four steps. First, point a remote interpreter at the Docker container so PHP and every extension are identical to the production environment. Second, set up separate Run Configurations for unit tests, integration tests, and the full suite, and check them into version control as XML. Third, use test filters consistently: run individual test methods with one click, define groups for slow tests, and protect the fast feedback loop. Fourth, use coverage deliberately: no overhead in the TDD cycle, PCOV for fast local analysis, Xdebug with branch coverage for the CI report.

Many teams are effectively testing in the dark: they do not know which code paths are covered and which are not, because coverage integration is missing or too slow. PhpStorm with a correctly configured remote interpreter eliminates that problem: coverage becomes visible in the editor, directly on the lines it affects, with no need to open HTML reports manually or interpret raw output.

PHPUnit in PhpStorm: The essentials at a glance

Remote interpreter

Set up the Docker container as the PHP source: same PHP version, same extensions, same php.ini as in the production environment.

Run Configurations

Separate configurations for unit, integration, and full suite. Version them as XML under .idea/runConfigurations/.

Test filter

Run individual methods with one click. Use group attributes for slow tests and exclude them from fast suites.

Coverage strategy

TDD without coverage (speed), PCOV for local analysis, Xdebug branch coverage for the CI report. Never enable coverage during the TDD cycle.

9. FAQ: PHPUnit in PhpStorm

1PHP interpreter not found in Docker?
Check the Docker socket (unix:///var/run/docker.sock). After an image rebuild, re-read the interpreter under Settings → PHP. Verify path mappings on both the local and container sides.
2Coverage analysis failing?
Set XDEBUG_MODE=coverage inside the container. Enable only one coverage extension (Xdebug or PCOV, never both). Check the PHPUnit version against driver compatibility.
3Sharing Run Configurations with the team?
Check .idea/runConfigurations/*.xml into git. Keep workspace.xml and other personal PhpStorm files in .gitignore.
4--filter vs. --testsuite?
--filter: regex on method names, flexible for individual tests. --testsuite: structured selection from phpunit.xml, for stable suite separation.
5Enabling PCOV instead of Xdebug?
pecl install pcov in the Docker image. Disable Xdebug. PhpStorm detects PCOV automatically. Only one coverage extension may be loaded at a time.
6PHPUnit slower in PhpStorm than on the CLI?
Turn off coverage (--no-coverage), set XDEBUG_MODE=off, and configure path mappings correctly to minimize file transfers.
7Rerunning only failed tests?
In PhpStorm: 'Rerun Failed Tests' in the test results view. CLI: enable --order-by=defects together with --cache-result.
8Seeing coverage gaps directly in the editor?
After a coverage run, PhpStorm automatically enables line highlighting. Red lines mean not covered. Toggle 'Show Coverage Highlighting' or press Alt+F11 for the report.
9Validating PHPUnit attributes in PhpStorm?
PhpStorm recognizes #[Test], #[CoversClass], #[DataProvider], and others. Misspelled classes are flagged as errors. PHPUnit must be installed via Composer.
10Integrating the coverage report into GitLab CI?
Generate --coverage-clover or --coverage-cobertura, store it as an artifact. In .gitlab-ci.yml, set the coverage regex against the PHPUnit output: '/\d+\.\d+ %/'.