and Magento Projects
Using PHPUnit in PhpStorm is trivial when PHP is installed locally. With Docker interpreters, Magento-specific bootstrap files and integration tests that require a running database, the configuration becomes more complex. This guide shows how to solve each of these cases cleanly.
Table of Contents
- 1. The Challenge: PHPUnit in Complex Environments
- 2. Setting Up a Remote Interpreter for PHPUnit in Docker
- 3. Creating PHPUnit Run Configurations in PhpStorm
- 4. Unit Tests: Fast, Isolated, No Database Access
- 5. Integration Tests in Magento: Bootstrap and Database Setup
- 6. Displaying and Evaluating Code Coverage in PhpStorm
- 7. Filtering, Rerunning and Debugging Tests
- 8. Test Fixtures and Data Providers for Clean Tests
- 9. Test Types in Magento 2 Compared
- 10. Summary
- 11. FAQ
1. The Challenge: PHPUnit in Complex Environments
Using PHPUnit in PhpStorm with a local PHP interpreter is straightforward: select the test framework, point it at PHPUnit, create a run configuration, done. But as soon as PHP runs inside a Docker container, everything changes. PhpStorm has to execute PHP commands inside the container, resolve path mappings between the local file system and container paths, and stream test output correctly into the IDE window. It gets even more complex with Magento 2: unit tests need a specific bootstrap, and integration tests need a running database and a preconfigured Magento installation inside the container.
The most common problems without a clean configuration: PhpStorm cannot find PHPUnit inside the container, test output does not appear in the IDE panel, coverage reports are not generated, and breakpoints in tests are never hit because Xdebug is not active in the test context. Each of these problems has a concrete solution that has nothing to do with the test code itself, only with the IDE configuration. Once this configuration is set up cleanly, you get a lasting benefit: a test environment that fits seamlessly into the development workflow.
2. Setting Up a Remote Interpreter for PHPUnit in Docker
Under Settings → PHP → CLI Interpreter you create a Docker interpreter: select From Docker, Vagrant, VM, WSL... and choose the phpfpm service from compose.yaml. PhpStorm connects to Docker and reads the PHP configuration from the container. This interpreter is then selected under Settings → PHP → Test Frameworks → PHPUnit as the base interpreter for PHPUnit runs. Important: PHPUnit must be available inside the container, either as a Composer dependency (vendor/bin/phpunit) or as a directly installed binary.
For the path to PHPUnit under PHPUnit Local (despite the confusing name, it is the path in the container context) you enter /var/www/html/vendor/bin/phpunit, i.e. the path as it exists inside the container. PhpStorm automatically translates this to the local file via path mappings. The alternative, PHPUnit by Remote Interpreter, is the more explicit option, where you directly specify the Docker interpreter and the container path to PHPUnit. For newly set up projects this option is clearer because it makes the connection between the interpreter and the PHPUnit binary explicit.
<?php
// phpunit.xml.dist (PHPUnit configuration for Mironsoft Magento 2 project)
// Located at: src/dev/tests/unit/phpunit.xml.dist
// <?xml version="1.0" encoding="UTF-8"?>
// <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
// xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd"
// bootstrap="framework/autoload.php"
// colors="true"
// beStrictAboutOutputDuringTests="true"
// stopOnError="false"
// stopOnFailure="false">
// <testsuites>
// <testsuite name="Mironsoft_Unit">
// <directory>../../../app/code/Mironsoft/*/Test/Unit</directory>
// </testsuite>
// </testsuites>
// <coverage>
// <include>
// <directory suffix=".php">../../../app/code/Mironsoft</directory>
// </include>
// <exclude>
// <directory>../../../app/code/Mironsoft/*/Test</directory>
// </exclude>
// </coverage>
// </phpunit>
// In PhpStorm: Run → Edit Configurations → PHPUnit
// Test runner: defined in configuration file
// Configuration file: /var/www/html/dev/tests/unit/phpunit.xml.dist (container path)
3. Creating PHPUnit Run Configurations in PhpStorm
Run configurations in PhpStorm are saved settings for a specific test run. For a Magento project it is worth creating at least three run configurations: one for all unit tests in the project (using phpunit.xml.dist), one for all unit tests of a specific module (filtered to a folder), and one for integration tests (using a different phpunit.xml). These configurations can be created under Run → Edit Configurations → PHPUnit and are stored as XML files under .idea/runConfigurations/, which means they can be committed to the repository.
Important settings in every run configuration: select the correct Docker interpreter, specify the path to phpunit.xml as a container path, and enter under Environment Variables any variables the test bootstrap needs. For Magento this often means TESTS_CLEANUP=disabled for faster integration test runs, or MFTF_UTILS=0 to skip MFTF-specific initializations. The run configurations then appear in the toolbar and can be started with a single click or the shortcut Shift+F10.
4. Unit Tests: Fast, Isolated, No Database Access
By convention, unit tests in Magento 2 live under app/code/Vendor/Module/Test/Unit/ and use no database connection, no Magento service container and no real DI instances. Instead, all dependencies are passed in as mocks. PHPUnit 11 combined with PHP 8.4 and strict types allows for particularly precise test code organization: return type declarations on mock methods, intersection types for mocked interfaces, and constructor promotion in the test classes themselves.
In PhpStorm, a single unit test runs with one click on the green play icon in the gutter next to the test method or the test class. PhpStorm automatically opens the test result panel and shows pass/fail status, assertion details on failures, and the runtime of each test. Via Run → Run Tests in Parallel you can run multiple tests at once for larger test suites, with PhpStorm automatically managing the parallel container starts. The result panel also lets you filter failed tests directly and rerun only those.
<?php
declare(strict_types=1);
namespace Mironsoft\Catalog\Test\Unit\Model;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Api\Data\ProductInterface;
use Mironsoft\Catalog\Model\ProductService;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
/**
* Unit test for ProductService, no database, no real DI container.
* Runs in PhpStorm via gutter icon or run configuration.
*/
class ProductServiceTest extends TestCase
{
private ProductService $productService;
private ProductRepositoryInterface&MockObject $repositoryMock;
protected function setUp(): void
{
// Create mock using intersection type (PHP 8.1+)
$this->repositoryMock = $this->createMock(ProductRepositoryInterface::class);
$this->productService = new ProductService(
productRepository: $this->repositoryMock,
);
}
/**
* @test
*/
public function getBySku_returnsProduct_whenFound(): void
{
$productMock = $this->createMock(ProductInterface::class);
$productMock->method('getSku')->willReturn('test-sku');
$this->repositoryMock
->expects(self::once())
->method('get')
->with('test-sku')
->willReturn($productMock);
$result = $this->productService->getBySku('test-sku');
self::assertSame($productMock, $result);
}
}
5. Integration Tests in Magento: Bootstrap and Database Setup
Magento's integration tests live under dev/tests/integration/ and require a full Magento installation with database access. The bootstrap (dev/tests/integration/framework/bootstrap.php) starts Magento without a web server, initializes the database from a separate test database, and provides the full DI container. The test database connection is configured via dev/tests/integration/etc/install-config-mysql.php.
In PhpStorm, you configure a dedicated run configuration for integration tests that points to dev/tests/integration/phpunit.xml.dist and uses the Docker interpreter. The most important difference from the unit test configuration: the test database connection must be reachable inside the container. In a Mark Shust setup this means using the MariaDB service name as the database host (db or mysql), not localhost. Integration tests take considerably longer than unit tests, and PhpStorm shows the progress in the test panel and stops on configured abort conditions such as stopOnFailure.
6. Displaying and Evaluating Code Coverage in PhpStorm
Code coverage shows which lines of code are covered by tests: green for covered, red for not covered. In PhpStorm you enable coverage via the coverage button (the shield icon) next to the run button, or via Run → Run with Coverage. PhpStorm then starts PHPUnit with Xdebug or PCOV as the coverage driver and shows the result directly in the editor as a colored margin. At the same time, the coverage panel opens with percentage values per class and method.
Important for the coverage configuration: Xdebug must run in coverage mode (XDEBUG_MODE=coverage), or PCOV must be installed as a faster alternative. PCOV is significantly faster than Xdebug for pure coverage measurements, but it cannot be used for debugging at the same time. In phpunit.xml you specify which directories are included in the coverage measurement and which are excluded. For Magento this means: include your own modules, exclude Magento core and Composer packages.
7. Filtering, Rerunning and Debugging Tests
PhpStorm lets you filter and rerun tests in several ways. In the test result panel there are buttons to show only failed tests, only running tests, or only ignored tests. Right-clicking a test suite or a single test in the panel opens a context menu with Rerun options. You can also right-click the gutter icon directly in the editor code and start the test with a specific run configuration.
For debugging tests, the integration with Xdebug in PhpStorm is especially valuable. Instead of Run you use Debug (the bug button), and PhpStorm starts PHPUnit with Xdebug enabled. Breakpoints in test methods and in the code under test are hit correctly, and the debugger shows the full call stack and all variables. This is particularly helpful for complex unit tests with many mock interactions, where you want to understand which mock methods are called in which order.
8. Test Fixtures and Data Providers for Clean Tests
Data providers in PHPUnit 11 are methods or static methods (recommended in PHPUnit 11) that return test data as an iterator or array. PhpStorm fully supports data providers: in the test result panel, each dataset appears as its own entry with the data provider key as the label. Failures show exactly which dataset caused the problem. This makes data provider tests much easier to debug than loops inside test methods.
For Magento integration tests there is the fixture system with the @magentoDataFixture and @magentoAppIsolation annotations. These annotations load PHP files that insert test data into the database and ensure that after each test the database is reset to a defined state. PhpStorm cannot resolve these annotations directly, since they are Magento-specific PHPUnit extensions, but code navigation to the fixture files works via Ctrl+Click on the file name in the annotation value.
<?php
declare(strict_types=1);
namespace Mironsoft\Pricing\Test\Unit\Model;
use Mironsoft\Pricing\Model\PriceCalculator;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
/**
* Tests PriceCalculator with data providers, each dataset visible separately in PhpStorm.
*/
class PriceCalculatorTest extends TestCase
{
private PriceCalculator $calculator;
protected function setUp(): void
{
$this->calculator = new PriceCalculator();
}
/**
* Static data provider, recommended in PHPUnit 11.
*
* @return array<string, array{float, float, float}>
*/
public static function priceProvider(): array
{
return [
'standard price' => [100.0, 0.19, 119.0],
'reduced rate' => [200.0, 0.07, 214.0],
'zero tax' => [50.0, 0.0, 50.0],
'fractional result' => [33.33, 0.19, 39.66],
];
}
#[DataProvider('priceProvider')]
public function testGrossPriceCalculation(
float $net,
float $taxRate,
float $expectedGross,
): void {
// Each dataset appears as separate entry in PhpStorm test panel
self::assertEqualsWithDelta(
$expectedGross,
$this->calculator->calculateGross($net, $taxRate),
0.01,
);
}
}
9. Test Types in Magento 2 Compared
Magento 2 distinguishes several test levels with different requirements for runtime, infrastructure and PhpStorm configuration. Choosing the right test type has a direct effect on how fast you get feedback from PhpStorm.
| Test Type | Runtime | Infrastructure | PhpStorm Setup |
|---|---|---|---|
| Unit Tests | Milliseconds | PHP in container only | Simple, phpunit.xml.dist |
| Integration Tests | Seconds to minutes | PHP + database + Magento | Dedicated run configuration |
| API Functional Tests | Seconds to minutes | Full Magento instance | Separate configuration |
| MFTF (Functional) | Minutes to hours | Browser + Selenium + Magento | External, CLI only |
| Performance Tests | Hours | JMeter + Magento | Not in PhpStorm |
For day-to-day development work with PhpStorm, unit tests and integration tests are the relevant levels. Unit tests can be started directly from the editor with the gutter icon and deliver feedback in milliseconds. Integration tests are best started via a run configuration for the whole module and run before creating a pull request. MFTF and performance tests belong in the CI pipeline and are not run interactively in PhpStorm.
Mironsoft
Magento 2 testing, PHPUnit setup and test automation
Ready to use PHPUnit productively in Magento and Docker?
We set up PHPUnit for your Magento project: Docker interpreter, run configurations for unit and integration tests, coverage reporting and pre-commit hooks, so testing is standard practice, not extra effort.
Test Setup
Configure PHPUnit correctly with Docker interpreter and Magento bootstrap
Coverage Reporting
Set up PCOV or Xdebug for coverage, HTML reports and PhpStorm integration
Test Strategy
Split unit vs. integration tests sensibly and integrate into the CI pipeline
10. Summary
Using PHPUnit cleanly in PhpStorm with Docker and Magento requires a one-time configuration effort in four areas: setting up the Docker interpreter correctly, creating run configurations for different test types, enabling coverage reporting, and accounting for Magento-specific bootstrap requirements. Once configured, tests can be started with a click or a shortcut, results appear directly in the IDE panel, and coverage is shown as a colored overlay in the editor.
The biggest effect lies in speeding up the test feedback loop. Instead of starting tests in a separate terminal with CLI commands and scrolling through output, you start the test directly from the editor, see the result immediately in the IDE panel, and can navigate to the failing assertion with a single click on failure. This makes testing a natural part of the development workflow instead of a separate step performed after writing code.
PHPUnit in PhpStorm: The Essentials at a Glance
Docker Interpreter
Settings → PHP → CLI Interpreter → From Docker. PHPUnit path in container: /var/www/html/vendor/bin/phpunit. Set path mappings correctly.
Run Configurations
Separate configurations for unit and integration tests. Commit as XML under .idea/runConfigurations/ to the repository.
Coverage
PCOV for fast coverage, Xdebug for coverage plus debugging. Set XDEBUG_MODE=coverage. Coverage appears as a colored overlay in the editor.
Magento Integration Tests
Configure install-config-mysql.php with the Docker database host. Use the service name as host, not localhost. Dedicated run configuration with correct bootstrap.