Running PHPUnit Tests with Code Coverage in PHPStorm When Working in Docker

Running PHP Unit tests with code coverage in PhpStorm

In PhpStorm, there is a possibility to create Run/Debug Configurations and to run them from IDE. It allows the project participants to share common configurations using a ‘Shared’ flag in the settings. One of such operations is running unit tests in the course of development. Let’s discuss how to fine-tune this process by means of Docker.

There is a plugin PHPUnit Code Coverage which is used to display the code coverage with the PhpStorm tests. PhpUnit can be run with the options for generating code coverage report. This report will contain the information about the number of calls to each operator during tests execution. Using these statistics, we can calculate the percentage of the test code coverage since if there are operators in the file which have never been called during the tests, then, this piece of code is not covered by tests. That’s what the plugin in the form of a table with file and directory navigation shows. The plugin also can highlight in green or red the lines in your files which, correspondingly, are covered or not covered by tests:

PHPUnit text highlighting

For this plugin to work correctly in PhpStorm with PHP CLI configured from the Docker container, a range of additional actions should be taken.

First, so far, the work with the already running containers has not been implemented correctly yet. It is a well-known drawback which is discussed here. There is an issue where users suggest possible solutions, and there is also an issue where the fix for this problem is being planned. I recommend you to subscribe to them to remain informed. Briefly, the matter is that when you set up running of Run Configuration from Docker, PhpStorm restarts the container with PHP any time the script is performed in it, and after that, the container is stopped. It can be observed from the results of test running from PhpStorm:

Testing started at 12:47 PM ...
docker-compose://[/home/user_name/sites/project_name/docker-compose.yml]:php_cli/php -dxdebug.coverage_enable=1 /app/vendor/phpunit/phpunit/phpunit --coverage-clover /opt/phpstorm-coverage/ProjectName$Run_all_tests.xml --configuration /app/phpunit.xml.dist --teamcity
Recreating project_name_php_cli_1 ...          <----- here the container is recreated
Attaching to project_name_php_cli_1
PHPUnit 7.3.1 by Sebastian Bergmann and contributors.
 
Recreating project_name_php_cli_1 ... done
 
Time: 7.9 seconds, Memory: 42.00MB
 
OK (689 tests, 1663 assertions)
 
Generating code coverage report in Clover XML format ... done
project_name_php_cli_1 exited with code 0
Aborting on container exit...                  <----- here the container is stopped
 
Process finished with exit code 0

This approach makes permanent development impossible. When you write tests and features in IDE and then run tests, and after that switch to the browser and test the feature manually, you need to restart PHP container by hand because PhpStorm stops it after running your console script from the Run/Debug menu. To evade this side effect till JetBrains developers solve it, the community has worked out the easiest solution. You need to create another PHP container which will be used solely for Run/Debug Configurations in PhpStorm. IDE will run it, stop it or whatever. At the same time, your major PHP container will not be killed, and you will be able to run your code from the web browser (or any other client for HTTP calls). The configuration, in this case, looks like this:

docker-compose.yml

version: '2'
services:
    php:
        image: jekakm/php71-core:201807161
        environment:
            - "PHP_IDE_CONFIG=serverName=project-name-docker"
        volumes:
            - "./docker-configs/php-fpm.ini:/etc/php/7.1/fpm/php.ini"
            - "./docker-configs/php-cli.ini:/etc/php/7.1/cli/php.ini"
            - ".:/app:cached"
 
    # This container should be used for running php scripts inside PhpStorm.
    # Because PhpStorm recreates the php container every time it runs a script.
    # It is a temporary solution until it be fixed in new releases of PhpStorm.
    php_cli:
        build:
            context: ./docker-configs/php-image
        environment:
            - "PHP_IDE_CONFIG=serverName=project-name-docker"
        volumes:
            - "./docker-configs/php-cli.ini:/etc/php/7.1/cli/php.ini"
            - ".:/app:cached"

Note. jekakm/php71-core:201807161 image is our studio image for PHP container. You may use any other image for PHP as a basis.

php_cli container should be inherited from the PHP container or be its copy. Depending on the exact PHP image and exact platform, the root access within the container is necessary for the correct work of the code coverage plugin. We have solved this problem by adding an additional instruction in our image for the console PHP. If, during the code coverage generation, PhpStorm notifies you that it can’t get access to a certain category, then, you need to add into the ./docker-configs/php-image/Dockerfile file the following:

./docker-configs/php-image/Dockerfile

FROM jekakm/php71-core:201807161
USER root

Now, we need to set up and choose PHP Server from Docker for our scripts from Run/Debug. For this purpose, in the PhpStorm’s settings, choose «Languages & Frameworks →PHP→CLI Interpreter» and add a new server. Also, tick the «Visible only for this project» option since for every project you should have a separate docker-compose.yml to avoid inter-project connection sharing in PhpStorm. Choose configuration for Docker Composer and then, in the Service drop-down menu, choose the one we have created, php_cli:

server

You also need to go to Languages & Frameworks → PHP → Test Frameworks settings and create a new configuration for PHPUnit by Remote Interpreter. In the drop-down list of interpreters, choose the one we set up earlier, PHP 7.1 Docker Compose. You can configure the PHPUnit settings by default. Since the tests are run from Docker, we need to specify the paths to the files regarding their location in the container. In our case, the project is in the /app folder, that’s why the path to the autoloader is specified as /app/vendor/autoload.php, and the path to the configuration file is /app/phpunit.xml.dist. It is important to specify this path correctly because there won’t be a clue from PhpStorm.

settings

We should also mention that for PhpStorm to process the file with coverage result correctly, you need to specify the path to the files correctly. For this, in the Test Frameworks settings for the configuration setted up for Docker, you will have to add a new rule into the Path mappings. The local path should refer to the directory where the source code is stored locally. In the example, it is home/user/sites/project_name. Remote Path is a path to the root of your project in the Docker container. In our example it is /app:

path

This should be done because the code coverage report stores the paths to the files which were processed by PHPUnit. The report generated in Docker, correspondingly, preserves the paths to the files regarding their location in the container. And when the file which is mentioned in the report is open in PhpStorm, the PhpStorm highlights line coverage in it. On the contrast, if the local file path and the report file path do not correspond, the PhpStorm will not understand that the Docker container path complies with the local path and the file should be highlighted.

After all setups, we reassemble containers, run them and work with a project in the browser. Simultaneously we run tests in PhpStorm. They are performed in the php_cli container and do not affect the PHP container. The deployment of a separate container to run console utilities is not limited by PHPUnit. Now that you have a configured interpreter from the php_cli container, you can create other Run/Debug Configurations in PhpStorm, for example, any PHP Script which will also be run in this short-lived container. Thus you can see the implementation result immediately in IDE without having to switch to the terminal and type in the command manually. It also allows the optimization of the development process since in Run/Debug Configurations you can assign dedicated hotkeys in PhpStorm so that the running of the routine tasks will be confined to the shortcut on your keyboard.

Another possible source of problems with Docker inside PhpStorm is a docker-compose utility. Don’t forget to update it. I myself encountered the situation when PhpStorm didn’t want to run it because I had a too early version installed on my computer. The update to the current version solved this problem. You can check for the current version here, and you can find the guidelines for the docker-compose update here.

P.S. If JetBrains developers eventually improve the behavior of the docker-compose utility in their IDE (well, this issue is urgent not only for PhpStorm), there will no longer be any need for the approach described.