If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeFind your terminal. But this time, run phpunit with a -h
option:
./vendor/bin/phpunit -h
Woh! That is a lot of options. Ok, let's talk about every single one of them. Just kidding! I've never even used most of these options! But there are a few awesome flags that will be very useful.
Google for "PHPUnit cli" to find their page about this. It's a bit prettier than reading the terminal. At the top of the output, it says that you can pass one or more files or directories. That's useful to run just one test class or all the classes in one directory. For example, we could run just the DinosaurFactoryTest
:
./vendor/bin/phpunit tests/AppBundle/Factory/DinosaurFactoryTest.php
Oh, and before I forget, Google for "Symfony PHPUnit Bridge" to find a special component that lives in Symfony.com: symfony/phpunit-bridge
.
This is a wrapper around PHPUnit that adds a couple of extra features like deprecation reporting that will tell you about deprecated code paths that you're using during your tests.
Tip
In Symfony 4, this is the officially-recommended way to use PHPUnit. You should install this instead of installing PHPUnit directly.
Basically, after you install this, you'll use vendor/bin/simple-phpunit
to activate it. It supports all the same options.
Back on the PHPUnit docs, my favorite option by far is --filter
. This let's you run just one test method, and it's critical when you're trying to debug one test.
If you scroll down, the docs show a bunch of examples. Usually, I copy the method name I'm testing, pass --filter
and then paste that name. But you can get a lot fancier.
Let's try a few! First, re-run the test with another flag: --debug
:
./vendor/bin/phpunit tests/AppBundle/Factory/DinosaurFactoryTest.php --debug
The output now tells us which tests are running. Let's run just one of them:
./vendor/bin/phpunit --filter testItGrowsADinosaurFromASpecification --debug
And... yes! It ran three tests, because this one method has a data provider. But sometimes, you'll need to debug just one test case of a provider. Surround the method name in quotes, and then add #1
:
./vendor/bin/phpunit --filter 'testItGrowsADinosaurFromASpecification #1' --debug
Ah, so cool! You can also use @default response
- that's the test case that we gave a special name.
./vendor/bin/phpunit --filter 'testItGrowsADinosaurFromASpecification @default response' --debug
For the longest time, I didn't know you could do this. I love it.
If you're running a lot of tests, you can tell PHPUnit to stop immediately when one of them has an error or fails... instead of waiting for all of them to finish running.
To do that, use the --stop-on-failure
and --stop-on-error
options:
./vendor/bin/phpunit --stop-on-failure --stop-on-error
We don't have any errors - yes! - but you get the idea!
There are many other options... but you can do even more in the configuration file! And we already have one! PHPUnit automatically looks for a phpunit.xml
file and then a phpunit.xml.dist
file.
This configures a few really important things... like bootstrap
. Thanks to this, PHPUnit requires the autoloader before running the tests. But there is so much more you can do: tweak php.ini
settings, set environment variables, configure test suites or tweak code coverage setup.
Most of the time, you'll have just one test suite... which means that all the test are always executed. But you could, for example, separate your unit, integration and functional tests into different suites so that you could run them independently. That's kind of cool, because integration and functional tests are much slower than unit tests.
Let's do one quick example: add a test suite called entities
and set its directory to tests/*Bundle/Entity
.
To run this suite, use:
./vendor/bin/phpunit --testsuite entities --debug
It runs only those tests.
Ok, enough of this! Let's get back to work and talk about integration tests: what they are, and when they can save your life.
PHP
vagrant@ubuntu-xenial:/vagrant$ php -v
PHP 7.2.4-1+ubuntu16.04.1+deb.sury.org+1 (cli) (built: Apr 5 2018 08:53:57) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.2.4-1+ubuntu16.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
with Xdebug v2.6.0, Copyright (c) 2002-2018, by Derick Rethans
Symfony:
vagrant@ubuntu-xenial:/vagrant$ php bin/console about
-------------------- --------------------------------------
Symfony
-------------------- --------------------------------------
Version 3.3.10
End of maintenance 01/2018 Expired
End of life 07/2018
-------------------- --------------------------------------
Kernel
-------------------- --------------------------------------
Type AppKernel
Name app
Environment dev
Debug true
Charset UTF-8
Root directory ./app
Cache directory ./var/cache/dev (563 KiB)
Log directory ./var/logs (8 KiB)
-------------------- --------------------------------------
PHP
-------------------- --------------------------------------
Version 7.2.4-1+ubuntu16.04.1+deb.sury.org+1
Architecture 64 bits
Intl locale n/a
Timezone UTC (2018-04-19T18:07:19+00:00)
OPcache true
APCu false
Xdebug true
-------------------- --------------------------------------
Actually, I cloned this repo https://github.com/knpunive... and packed it in Virtualbox VM. Here are my Vagrantfile and build script for it:
https://bitbucket.org/renta...
https://bitbucket.org/renta...
// composer.json
{
"require": {
"php": "^7.0, <7.4",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/orm": "^2.5", // v2.7.2
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"sensio/distribution-bundle": "^5.0.19", // v5.0.21
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.28
"symfony/monolog-bundle": "^3.1.0", // v3.1.2
"symfony/polyfill-apcu": "^1.0", // v1.6.0
"symfony/swiftmailer-bundle": "^2.3.10", // v2.6.7
"symfony/symfony": "3.3.*", // v3.3.13
"twig/twig": "^1.0||^2.0" // v2.4.4
},
"require-dev": {
"doctrine/data-fixtures": "^1.3", // 1.3.3
"doctrine/doctrine-fixtures-bundle": "^2.3", // v2.4.1
"liip/functional-test-bundle": "^1.8", // 1.8.0
"phpunit/phpunit": "^6.3", // 6.5.2
"sensio/generator-bundle": "^3.0", // v3.1.6
"symfony/phpunit-bridge": "^3.0" // v3.4.30
}
}
In my case PHPUnit Bridge did not worked (out of the box and after the "composer require --dev symfony/phpunit-bridge" from a Symfony documentation):
vagrant@ubuntu-xenial:/vagrant$ ./vendor/bin/simple-phpunit
symfony/yaml is not required in your composer.json and has not been removed
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies
Package operations: 23 installs, 0 updates, 0 removals
- Installing phar-io/version (1.0.1): Downloading (100%)
- Installing phar-io/manifest (1.0.1): Downloading (100%)
- Installing phpunit/php-timer (1.0.9): Downloading (100%)
- Installing sebastian/global-state (2.0.0): Downloading (100%)
- Installing sebastian/recursion-context (3.0.0): Downloading (100%)
- Installing sebastian/object-reflector (1.1.1): Downloading (100%)
- Installing sebastian/object-enumerator (3.0.3): Downloading (100%)
- Installing sebastian/resource-operations (1.0.0): Downloading (100%)
- Installing myclabs/deep-copy (1.7.0): Downloading (100%)
- Installing theseer/tokenizer (1.1.0): Downloading (100%)
- Installing sebastian/version (2.0.1): Downloading (100%)
- Installing sebastian/environment (3.1.0): Downloading (100%)
- Installing sebastian/code-unit-reverse-lookup (1.0.1): Downloading (100%)
- Installing phpunit/php-token-stream (2.0.2): Downloading (100%)
- Installing phpunit/php-text-template (1.2.1): Downloading (100%)
- Installing phpunit/php-file-iterator (1.4.5): Downloading (100%)
- Installing phpunit/php-code-coverage (5.3.2): Downloading (100%)
- Installing sebastian/exporter (3.1.0): Downloading (100%)
- Installing doctrine/instantiator (1.0.5): Downloading (100%)
- Installing phpunit/phpunit-mock-objects (5.0.6): Downloading (100%)
- Installing sebastian/diff (2.0.1): Downloading (100%)
- Installing sebastian/comparator (2.1.3): Downloading (100%)
- Installing symfony/phpunit-bridge (v4.0.8): Symlinking from /vagrant/vendor/symfony/phpunit-bridge
Writing lock file
Generating optimized autoload files
PHP Fatal error: Declaration of Symfony\Bridge\PhpUnit\TextUI\TestRunner::handleConfiguration(array &$arguments) must be compatible with PHPUnit\TextUI\TestRunner::handleConfiguration(array &$arguments): void in /vagrant/vendor/symfony/phpunit-bridge/TextUI/TestRunner.php on line 25
PHP Stack trace:
PHP 1. {main}() /vagrant/vendor/symfony/phpunit-bridge/bin/simple-phpunit:0
PHP 2. include() /vagrant/vendor/symfony/phpunit-bridge/bin/simple-phpunit:251
PHP 3. PHPUnit\TextUI\Command::main() /vagrant/vendor/bin/.phpunit/phpunit-6.5/phpunit:17
PHP 4. Symfony\Bridge\PhpUnit\TextUI\Command->run() /vagrant/vendor/bin/.phpunit/phpunit-6.5/src/TextUI/Command.php:148
PHP 5. Symfony\Bridge\PhpUnit\TextUI\Command->createRunner() /vagrant/vendor/bin/.phpunit/phpunit-6.5/src/TextUI/Command.php:161
PHP 6. spl_autoload_call() /vagrant/vendor/symfony/phpunit-bridge/TextUI/Command.php:31
PHP 7. Composer\Autoload\ClassLoader->loadClass() /vagrant/vendor/symfony/phpunit-bridge/TextUI/Command.php:31
PHP 8. Composer\Autoload\includeFile() /vagrant/vendor/bin/.phpunit/phpunit-6.5/vendor/composer/ClassLoader.php:322
PHP 9. include() /vagrant/vendor/bin/.phpunit/phpunit-6.5/vendor/composer/ClassLoader.php:444