Coverage Highlighting Right in the Editor Instead of Just the Report
AI generated
IDE
{ }
PhpStorm · PHPUnit · Coverage
Coverage Highlighting Right in the Editor
instead of clicking through an HTML report

An HTML coverage report is good for a bird's-eye view, but poor for actual work. PhpStorm shows test coverage directly in color inside the editor gutter, so uncovered lines are visible exactly where you are coding.

14 min read Code coverage PHPUnit Editor gutter

1. Why an HTML report alone slows the workflow down

A classic HTML coverage report, generated via PHPUnit and opened in a browser, gives a good overall picture of percentages per class and file. But for actual work on the code it means a constant context switch: open the report, find the file, identify the line, switch back to the editor, relocate the right spot. Every new test round restarts this cycle, which for iterative test development quickly costs more time than actually writing the tests.

PhpStorm solves this by projecting coverage data directly into the editor, exactly where you happen to be working anyway. Instead of switching back and forth between browser and IDE, your eyes stay on the code the whole time, while colored markers in the gutter, the narrow strip to the left of the line numbers, immediately show which lines were reached by the last test run and which were not.

2. Starting a coverage run from within PhpStorm

A PHPUnit test run with coverage collection is not started via the normal Run button but via the coverage button next to it, recognizable by its shield icon, or via the context menu option Run with Coverage. PhpStorm uses either Xdebug or PCOV as the coverage driver in the background, depending on which extension is enabled in the project's PHP interpreter and configured as the coverage engine in the PHP settings.

In Docker-based Magento setups, as commonly used with Mark Shust's docker-magento, the PHP interpreter configured in PhpStorm must point at the container's interpreter for Xdebug or PCOV to actually take effect in the right context. A common pitfall is that Xdebug is enabled in the container for debugging, but the coverage mode is missing from the xdebug.mode setting, which lets PhpStorm run the test but delivers no coverage data at all.


# In php.ini / docker/php.ini of the Magento container:
# xdebug.mode must include "coverage", otherwise the editor gutter stays empty
xdebug.mode=develop,debug,coverage

# Alternative with PCOV, noticeably faster for pure coverage runs:
# pcov.enabled=1
# pcov.directory=/var/www/html/app/code

# after a change in php.ini, restart the PHP-FPM process:
bin/restart

3. Reading the coverage colors in the gutter correctly

After a coverage run, PhpStorm colors every line in the editor gutter one of three ways: green for lines executed at least once, red for lines never reached during the test run, and a neutral gray or uncolored state for lines PHPUnit does not count as executable code at all, such as plain comments or blank lines. This three-way split makes it immediately visible where gaps remain in a class, without reading a single percentage.

The combination with conditional branches is especially revealing: an if statement can appear partially green when only one branch was executed, while the else block stays red. This line-level granularity is the real advantage over the HTML report, which shows colored lines too, but only after a context switch away from the code currently being worked on.

4. Seeing live what the current test case actually covers

While writing a new test case, it pays to run coverage specifically for the affected test class rather than the entire suite. That reduces run time considerably and immediately shows whether the test just written actually reaches the intended code path. A test that should turn a line green as expected but leaves it red is a reliable signal that either an assertion is missing or the test case hits an entirely different line than assumed.

This tight feedback loop noticeably changes the testing workflow: instead of writing tests and only afterward checking via a separate report whether they actually engage, you can see directly after every test run, in the same editor window, which lines just turned green. For more complex methods with several branches, that is the difference between guessing and actually verified test behavior.

5. Workflow: closing uncovered areas in a targeted way

An efficient approach for existing code with low coverage is to first run coverage over the relevant class or module and then work top to bottom through the red-marked lines. For each red block, a minimal, targeted test case is written that triggers exactly that path, followed by another quick coverage run to confirm the line has turned green.

This iterative approach works particularly well for legacy code in grown Magento modules, where achieving full test coverage in one shot is unrealistic. Instead of chasing an abstract percentage that only updates after the full test run, you work line by line through the code that is actually risky and untested, which delivers the biggest safety gain per minute invested in testing.


// Example: after the coverage run, the else branch is marked red
public function calculateDiscount(float $price, ?CustomerGroup $group): float
{
    if ($group !== null && $group->isWholesale()) {
        return $price * 0.85; // green: covered by an existing test
    }

    return $price; // red: no test with $group === null yet
}

// Targeted test case that hits exactly the red line:
public function testCalculateDiscountReturnsFullPriceWithoutGroup(): void
{
    $result = $this->calculator->calculateDiscount(100.0, null);
    $this->assertSame(100.0, $result);
}

6. Comparing Xdebug and PCOV coverage modes

Xdebug delivers very detailed coverage data including branch and path coverage, but is noticeably slower since it instruments every single line of code during execution. In large test suites in Magento projects with several hundred test cases, this overhead can stretch the run time of a full coverage run from minutes to a multiple of that, undermining the live feedback character of the editor gutter when every run takes too long.

PCOV is purpose-built for coverage collection and considerably faster than Xdebug in coverage mode, but only delivers line coverage instead of branch coverage, which in practice is entirely sufficient for the line coloring shown in the editor gutter. For the daily live workflow in the editor, PCOV is therefore usually the better choice, while a full Xdebug coverage run with branch detail remains reserved for occasional, detailed analysis.

7. Full suite versus a single test class: making the right choice

A coverage run across the entire test suite gives the most complete picture, but its run time rarely fits the immediate edit cycle. It makes more sense to run coverage only for the relevant test class, or a thematically matching test suite, while actively developing a class, and to run the full suite only before a commit or as part of the CI pipeline.

PhpStorm lets you save run configurations for exactly this purpose, for example a configuration that runs only the tests of a specific directory with coverage. These configurations can be re-triggered with a keyboard shortcut, so the coverage run over the relevant subset can be repeated after every code change with minimal effort, without reselecting paths every time.

8. Combining the coverage gutter with team-wide thresholds

The editor gutter does not replace automated coverage thresholds in the CI pipeline, but complements them well. While a CI job globally enforces that a module's overall coverage does not drop below a certain percentage, the local gutter helps precisely identify, during the actual development work, which new lines are putting that threshold at risk, before the push even happens.

In practice, a good combination is a strict CI threshold for newly added code paired with a looser requirement for existing legacy code, while the local gutter in PhpStorm serves as a fast, visual early-warning system during development. That way, a red line in the editor is often fixed long before the CI job ever reports a violation.

9. Practical tips for everyday work with the coverage gutter

The coverage gutter persists after a run until it is manually refreshed via Generate Coverage Report or another run, which occasionally leads to stale displays if code was changed in the meantime without rerunning the tests. A quick glance at the timestamp of the last coverage run in the coverage toolbar helps spot such stale displays before drawing the wrong conclusion about the actual test state.

For multi-module Magento projects, it is worth deliberately restricting the coverage scope in run configurations to the module currently being worked on, instead of accidentally instrumenting the entire app/code directory. That keeps run time low and prevents irrelevant coverage data from unrelated modules from diluting your own analysis or coloring the editor gutter in files that have nothing to do with the current task.

Coverage engine Speed Branch coverage Recommended use
Xdebug Slower Yes, detailed Occasional deep analysis of individual classes
PCOV Noticeably faster No, line coverage only Daily live workflow in the editor gutter
No driver active No coverage run possible Not available Set xdebug.mode or pcov.enabled in php.ini
Full suite with coverage Longer, complete picture Depends on engine Before a commit or in the CI pipeline
Single test class with coverage Very fast Depends on engine While actively developing a class

Mironsoft

PhpStorm setup, Docker integration, and team productivity

PhpStorm that actually runs optimally for Magento and PHP projects?

We review existing PhpStorm setups for slow indexing, unused Docker integration, and missing team conventions, then set up a configuration that is productive from the first second.

Setup Review

Optimizing indexing, interpreter, and memory settings for large Magento projects.

Docker Integration

Cleanly connecting Xdebug, PHPUnit, and database tools to the Docker setup.

Team Conventions

Standardizing inspection profiles, code style, and live templates project-wide.

10. Summary

Coverage Highlighting in the Editor: The Essentials at a Glance

Gutter color code

Green for executed, red for unreached, gray for non-executable lines.

PCOV for daily use

Noticeably faster than Xdebug in coverage mode, ideal for a tight live feedback loop.

Close gaps in a targeted way

Turn red lines green step by step, top to bottom, with minimal targeted test cases.

Complements CI thresholds

The local gutter warns during development, well before the CI pipeline reports a violation.

11. FAQ: Coverage Highlighting in the Editor: The Essentials at a Glance

1How do I start a PHPUnit test run with coverage in PhpStorm?
Via the coverage button next to the normal Run button, recognizable by its shield icon, or via Run with Coverage in the context menu. PhpStorm uses Xdebug or PCOV as the coverage driver.
2Why does the editor gutter stay empty after a test run?
Usually the coverage value is missing from xdebug.mode in php.ini, or neither Xdebug nor PCOV is enabled in the PHP interpreter being used. After changing php.ini, PHP-FPM needs to be restarted.
3What do the green, red, and gray colors in the coverage gutter mean?
Green shows lines executed at least once. Red shows lines never reached during the test run. Gray, or uncolored, marks lines PHPUnit does not count as executable code at all, such as comments.
4What is the difference between Xdebug and PCOV coverage?
Xdebug delivers detailed branch coverage but is noticeably slower. PCOV only delivers line coverage but is considerably faster and better suited to the daily live workflow in the editor.
5Should I always run coverage across the entire test suite?
Not during active development, since the run time disrupts the tight feedback loop. It is better to run coverage only for the test class currently being worked on, and run the full suite before a commit or in the CI pipeline.
6How do I tell if the coverage gutter is showing stale data?
A glance at the timestamp of the last coverage run in the coverage toolbar shows whether code has changed since then without rerunning the tests. In that case, simply rerun coverage.
7Does the editor gutter replace coverage thresholds in the CI pipeline?
No, both complement each other. The CI pipeline globally enforces a minimum value, while the local gutter immediately shows, visually, during development, which new lines are still untested.
8How do I systematically approach low test coverage in legacy code?
Work top to bottom through the red-marked lines, write a minimal targeted test case for each red block, and confirm with another coverage run that the line has turned green.
9How do I prevent unrelated modules from skewing the coverage gutter?
Deliberately restrict the coverage scope in the run configuration to the module currently being worked on, instead of instrumenting the entire app/code directory.
10Does coverage highlighting also work with Docker-based Magento setups?
Yes, as long as the PHP interpreter configured in PhpStorm points at the container and Xdebug with coverage mode enabled, or PCOV, is available there. After changes to php.ini, the container's PHP process needs to be restarted.