Instead of testing the whole codebase every time you, you might run the tests for the changed files only.
Mind you, it is not completely foolproof - having all tests for changed files green does not mean that all tests for the whole project will still be green! (It should be the case for unit tests, but not necessarily for other test types.)
Also, there are a few assumptions on the way necessary to make it all work as described.
Keeping all that in mind, let's do it!
Modified files list
First of all, we need a list of files that have been modified.
Assuming that we work in a feature branch which is supposed to be merged to develop
later on, we can get the list of changed files using:
$ git diff origin/develop --diff-filter=AM --name-only
config/packages/dev/monolog.yaml
src/Command/ExportCommand.php
src/Factory/DocumentRevisionManagerFactory.php
src/Normalizer/ArrayCollectionDenormalizer.php
--diff-filter=AM
will return only files that are added (A
) or modified (M
), while --name-only
will show only names of changed files.
Say we are interested only in files from src/
directory:
$ git diff origin/develop --diff-filter=AM --name-only | grep 'src/'
src/Command/ExportCommand.php
src/Factory/DocumentRevisionManagerFactory.php
src/Normalizer/ArrayCollectionDenormalizer.php
It would be great if we could pass just a list like this to PHPUnit's --filter
param, but we can't unofrtunately, as it works with class names only.
Let's then convert those filenames into class names:
$ git diff origin/develop --diff-filter=AM --name-only | grep 'src/' | sed 's/.*\///' | sed 's/\.php//'
ExportCommand
DocumentRevisionManagerFactory
ArrayCollectionDenormalizer
And finally merge them into a one-liner:
$ git diff origin/develop --diff-filter=AM --name-only | grep 'src/' | sed 's/.*\///' | sed 's/\.php//' | paste -sd '|' -
ExportCommand|DocumentRevisionManagerFactory|ArrayCollectionDenormalizer
Now that's something that we can pass to PHPUnit:
$ bin/phpunit --filter $(git diff origin/develop --diff-filter=AM --name-only | grep 'src/' | sed 's/.*\///' | sed 's/\.php//' | paste -sd '|' -)
Bash/Zsh Alias
But it's much too complex to type it each time we want to run it, so let's just convert it into an alias:
# ~/.aliases
alias utd="bin/phpunit --filter \$(git diff origin/develop --diff-filter=AM --name-only | grep 'src/' | sed 's/.*\///' | sed 's/\.php//' | paste -sd '|' -)"
(utd
stands for unit tests diff.)
Note the backslash \
before the whole git diff
command - if it wasn't there, the git diff
command would be executed when .aliases
file is loaded, and not when we call the alias. You will see the same thing used in the other aliases below.
All unit test-related aliases
In my case it is just one of several aliases related to unit testing:
# ~/.aliases
# Unit tests
export PHPUNIT="bin/phpunit --testsuite=Unit -v"
alias changed_files="export CHANGED_FILES=\$(git diff origin/develop --diff-filter=AM --name-only | grep 'src/' | sed 's/.*\///' | sed 's/\.php//' | paste -sd '|' -)"
alias ut="XDEBUG_MODE=off $PHPUNIT"
alias utf="ut --filter"
alias utd="changed_files; utf \$CHANGED_FILES"
# Unit test coverage
alias utc="XDEBUG_MODE=coverage $PHPUNIT --coverage-html=/tmp/coverage"
alias utcf="utc --filter"
alias utcd="changed_files; utcf \$CHANGED_FILES"
alias utco="open /tmp/coverage/index.html"
PHPUNIT
- base PHPUnit command definition used by other aliases, you might want to update it to adapt it to your project,ut
- runs all unit testsutf
- runs unit tests allowing to filter by manually provided class name, for exampleutf ArrayCollectionDenormalizer
changed_files
- prepares the$CHANGED_FILES
env variable, used by other aliasesutd
- runs unit tests for changed files only (diff)utc
- runs all unit tests and generates coverage reportutcf
- runs all unit tests and generates coverage report, allowing to filter by manually provided class nameutcd
- runs unit tests and generates coverage report for changed files only (diff)utco
- opens the generated coverage report