Automated testing is widely used to identify regressions in libraries and applications in different environments and with varying configuration.

No matter whether you employ [Test-Driven Development][1], have a set of acceptance tests from [Behaviour-Driven Development][2], or use a suite of functional tests to ensure your application works as you expect, the goal of automated testing is to quickly provide engineers with reliable information on potential problems.

Continous Integration (CI) is the practice of continuously (and automatically) testing every change a developer makes. So automated tests become an integral part of the development process providing direct feedback on changes made.

The Need For Testing

Traditionally, PHP projects did not have build processes. PHP is not a compiled language, so there was no need. However, because of advancements in front-end development, build processes are a common aspect of modern web projects. From minified and modularised JavaScript, CSS pre-processors, and so on. Because of this, using Composer to manage your PHP libraries is usually only an addition to the existing build process. While PHP libraries may not previously have needed a build process, testing their integration with dependencies now requires installing the dependencies through Composer.

To get the highest quality of feedback from automated tests, the environment the tests run in should match the target environment as closely as possible. For an application, this means that you should ideally share the same provisioning code (e.g. Chef, Puppet, Ansible, etc.) between your production and testing environment. You should also ensure that the versions of installed software matches. While for libraries, you should try to cover all supported platforms with as many different configurations as possible.

Using the Lock File for Tests

Davey Shafik’s article on [Composer’s Lock File][3] explains the typical usage of composer install and update . The key takeaway is that developers should run composer update manually to explicitly update individual dependencies while composer install should be used in automated processes. This principle includes automated test environments.

Using composer install with a lock file will reproduce the vendor directory in exactly the way it is in your production environment. This greatly reduces the risk of overlooking application bugs introduced through minor patch release updates of dependencies.

A library usually doesn’t have a single target platform that you could try to reproduce closely in your tests. Nevertheless, following the same principle and checking your composer.lock file into the library’s repository has some advantages:

When your automated tests fail, you can be certain that the failure does not result from installing a dependency with a different version than the one you used locally

When users run your tests on their platform and submit a bug report, you can be certain the issue is a result of the platform and not of the installed versions of dependencies

You explicitly document the precise versions you are testing against in your composer.lock file and your repository’s version history documents when this changes

When developing a library, you may explicitly want to support and test multiple versions of a dependency. For example, Symfony bundles usually support multiple versions of the Symfony framework.

Rather than trying to detect bugs randomly by having some users use different versions because there is no lock file in the repository, you can explicitly swap out these dependencies for a different version using composer require some/dependency:1.3.4 .

You can then do this for a predefined list of versions that you would like to run your test suite against. The require command updates the composer.json file and runs composer update some/dependency for you. But even in this case, the rest of the lock file remains unaltered, giving you all the advantages listed above.