Unit tests in PHP, Laradock and IntelliJ IDEA

Ben

September 3, 2020

Introduction

In my previous post (Using Laradock with JetBrains IDEA) I spoke about setting up Laradock and IntelliJ IDEA to use PHP in a containerised environment and how this would help with ensuring a clean environment and how it allows you to easily change PHP version and other extensions.

In this post I will quickly go over how to configure IntelliJ IDEA to use the Laradock containers for running unit tests, and why I recommend this approach.

Catch Up

If you did not read my previous post, or do not have Laradock setup yet, I recommend you go back and read it and setup Laradock for your environment as I will be referring to it in this post.

Re-cap

To configure IntelliJ IDEA to use Laradock for the tests we need to configure the following items:

  • Install the plugins - we need several plugins for IntelliJ IDEA, available on the marketplace
  • Configure the remote interpreter - using the PHP Remote Interpreter and PHP Docker plugin create a docker PHP remote interpreter
  • Validate Debug - Using the 'Languages & Frameworks -> PHP -> Debug' settings validate that debug is working in the docker

If all the above is configured correctly then you are ready.

Simple Projects

Once everything is configured and you have a project you want to create a test for, then in IntelliJ IDEA it is as simple as using the 'Create Test' shortcut, which on my configuration is 'Ctrl + Shift + T', when in the method, class or file you want to test. This will help you create a test for the item you want to test, and can help configure the test framework you are using.

Once you have the test you can simply click the small green arrow, or right click anywhere in the file and click run. Then IntelliJ IDEA will execute the test using the docker environment configured, and if debug is configured correctly then you can also immediately debug the tests as well.

Complex Projects

If you are thinking that this is too simple for a blog, then you are correct, then above is the easy case.

If you have a more complex PHP project that has dependencies that you are also working on, and want to run the tests that use those dependencies then you will start to get these strange errors, saying PHP cannot find files that you can clearly see exist.

PHP Warning:  require(/opt/project/vendor/composer/../intergral/coms-core-php-library/src/helpers.php): failed to open stream: No such file or directory in /opt/project/vendor/composer/autoload_real.php on line 66
PHP Stack trace:
PHP   1. {main}() /opt/project/vendor/phpunit/phpunit/phpunit:0
PHP   2. require() /opt/project/vendor/phpunit/phpunit/phpunit:59
PHP   3. ComposerAutoloaderInit529a7fbcfe0fdb131521dbb30af51cca::getLoader() /opt/project/vendor/autoload.php:7
PHP   4. composerRequire529a7fbcfe0fdb131521dbb30af51cca() /opt/project/vendor/composer/autoload_real.php:56
PHP Fatal error:  require(): Failed opening required '/opt/project/vendor/composer/../intergral/coms-core-php-library/src/helpers.php' (include_path='.:/usr/local/lib/php') in /opt/project/vendor/composer/autoload_real.php on line 66
PHP Stack trace:
PHP   1. {main}() /opt/project/vendor/phpunit/phpunit/phpunit:0
PHP   2. require() /opt/project/vendor/phpunit/phpunit/phpunit:59
PHP   3. ComposerAutoloaderInit529a7fbcfe0fdb131521dbb30af51cca::getLoader() /opt/project/vendor/autoload.php:7
PHP   4. composerRequire529a7fbcfe0fdb131521dbb30af51cca() /opt/project/vendor/composer/autoload_real.php:56

These are caused by the way IntelliJ IDEA mounts the folders into the docker container being used for the tests. There are a few ways to resolve these issues, and they all involve configuring the docker volumes used by the docker container.

Quick Fix

If you just want a quick fix then the easiest way is to quickly add the volume needed to the interpreter. This can be done by opening the interpreter settings 'Languages & Frameworks -> PHP' then opening the 'Docker container' settings for the interpreter, then under 'Volume bindings' add the volume required.

Docker Container Settings

The correct path for the volume can sometimes seem a bit strange, but what we are trying to do is replicate the same relative path to the dependency in the container as on your development machine. This is much easier if both the project you are testing, and the dependency are in the same parent directory (as mine are above). Both of my projects are inside the 'coms' directory on my machine (Host path), so inside the container I put them in the same relative locations (Container path).

If everything is setup correctly after this, you should then see that the error you got before has gone away!

Other solutions

The other solutions to this problem can make the problem a bit more complex (depending on your use case).

Symlink False

When defining the repository location for the local dependency in the composer.json file, the default behavior is to use a symbolic link to link the vendor location to the real source. This is what causes the docker containers to fail, as the symbolic link target is not inside the docker container, unless we add the volume mount. Fortunately compose comes with an option to not use a symbolic link, this will then copy the code into the vendor directory meaning docker will mount the directly correctly. This will however mean that any changes in the dependency will not be seen until a 'composer update' is run, slowing down the development.

If you favour this approach then you should be aware of this issue: https://github.com/composer/composer/issues/5048

Docker compose

Another option is to use a docker-compose file that defines the mounts that should be used by the interpreter. Then use the 'Docker compose' option when configuring the PHP interpreter. This will allow the different mounts that are needed to be defined in the code, so they can be shared or simply remembered. This approach can also have some issues, if for example you do not have a simple directory structure to the dependencies then sharing this can be complicated.

Although if you are working in a team this approach can mean that you can define the interpreter in a way that all team members can use it.

Why do all this?

I have found over the years, that if you cannot very easily run and debug the tests in the environment you are working in, it makes it much slower to produce quality. I have become accustomed to being able to execute a test directly in IntelliJ IDEA and when I started working on some PHP projects I needed to get this ability back. Although I will admit that the setup required for PHP is much more that say Node.Js, Python or even Java. I found that once it was all setup with Laradock and configured in IntelliJ IDEA, it all behaved the same as the other languages. Allowing me and the team to push forward with our work.

Ben

Ben

Experienced developer in various languages, currently a product owner of nerd.vision leading the back end architecture.