PHPUnit best practices to organize tests
I'll start of by linking to the manual and then going into what I've seen and heard in the field.
Organizing phpunit test suites
Module / Test folder organization in the file system
My recommended approach is combining the file system with an xml config.
tests/
\ unit/
| - module1
| - module2
- integration/
- functional/
with a phpunit.xml
with a simple:
<testsuites>
<testsuite name="My whole project">
<directory>tests</directory>
</testsuite>
</testsuites>
you can split the testsuites if you want to but thats a project to project choice.
Running phpunit
will then execute ALL tests and running phpunit tests/unit/module1
will run all tests of module1.
Organization of the "unit" folder
The most common approach here is to mirror your source/
directory structure in your tests/unit/
folder structure.
You have one TestClass per ProductionClass anyways so it's a good approach in my book.
In file organization
- One class per file.
It's not going to work anyways if you have more than one test class in one file so avoid that pitfall.
- Don't have a test namespace
It just makes writing the test more verbose as you need an additional use statement so I'd say the testClass should go in the same namespace as the production class but that is nothing PHPUnit forces you to do. I've just found it to be easier with no drawbacks.
Executing only a few tests
For example phpunit --filter Factory
executes all FactoryTests while phpunit tests/unit/logger/
executes everything logging related.
You can use @group
tags for something like issue numbers, stories or something but for "modules" I'd use the folder layout.
Multiple xml files
It can be useful to create multiple xml files if you want to have:
- one without code coverage
- one just for the unit tests (but not for the functional or integration or long running tests)
- other common "filter" cases
- PHPBB3 for example does that for
their phpunit.xmls
Code coverage for your tests
As it is related to starting a new project with tests:
- My suggestion is to use
@covers
tags like described in my blog (Only for unit tests, always cover all non public functions, always use covers tags. - Don't generate coverage for your integration tests. It gives you a false sense of security.
- Always use whitelisting to include all of your production code so the numbers don't lie to you!
Autoloading and bootstrapping your tests
You don't need any sort of auto loading for your tests. PHPUnit will take care of that.
Use the <phpunit bootstrap="file">
attribute to specify your test bootstrap. tests/bootstrap.php
is a nice place to put it. There you can set up your applications autoloader and so on (or call your applications bootstrap for that matter).
Summary
- Use the xml configuration for pretty much everything
- Seperate unit and integration tests
- Your unit test folders should mirror your applications folder structure
- To only execute specif tests use
phpunit --filter
orphpunit tests/unit/module1
- Use the
strict
mode from the get go and never turn it off.
Sample projects to look at
- Sebastian Bergmanns "Bank Account" example project
- phpBB3 Even so they have to fight some with their legacy ;)
- Symfony2
- Doctrine2
How to organize PHPUnit tests and autoloading with NetBeans IDE
Man, what a big question. :)
Ok, let's address some of the issues.
PHPUnit Skelgen tests in wrong path
That definitely is not mirror symmetric
True. It's an odd (and i think unresolved) path issue with skelgen.
Just move the generated *Test files into their correct folders.
Same question over here: PHPUnit, Netbeans and Symfony2 and wrong tests location
Folder structure for your project
Yes, this is the folder structure to work with:
src/
… my code
tests/
… my tests
vendor/
… third-party codeThe autoloading issue:
(Without Composer): your autoloader lives somewhere in
src
.
During your application bootstrap your require it, after that autoloading is setup for thesrc
folder. All classes in thesrc
folder are from now on autoloaded.Now over to tests: during the test bootstrap you include the autoloader from
src
and addsrc
andtest
. This makes all classes insrc
andtests
autoloadable.(with Composer): This whole issue gets a non-issue, if you work with Composer.
Composer has require and require-dev sections to define the vendor dependencies.
The autoloading is generated automatically, you simply include the Composer autoload file and done (one inclusion during src bootstrap and one inclusion during test boostrap). If the project is installed with development dependencies then those will be part of the autoloading, if not, they will not appear.No Hinting PHPUnit in NetBeans
Yes, the PHAR is not introspected or extracted, so Netbeans will not know the Class names of PHPUnit.
Solution 1: Extract the PHAR.
Solution 2: Add a PHP with classnames as aliases to guide Netbeans. I don't know if one is around. But it's a pretty common issue, chances are high that someone created it already.
Solution 3: Let me suggest Composer again: just pull phpunit as require-dev dependency into your project. The vendor path is scanned by Netbeans and the classnames will resolve.
What is the best way to organize Selenium tests with PHPUnit?
Yes, I prefer separate suite for each section of the site.
As you are using PHPUnit if you want to run tests parallel then you have to make one classes for one thread.
If you are using the section of site then you will have no Dependencies of one section of site with other. So that you can run the tests in parallel.
Create Section wise library and Suite so that you can run suites in Parallel.
Directory layout for PHPUnit tests?
I think it's a good idea to keep your files separate. I normally use a folder structure like this:
/myapp/src/ <- my classes
/myapp/tests/ <- my tests for the classes
/myapp/public/ <- document root
In your case, for including the class in your test file, why not just pass the the whole path to the include method?
include('/path/to/myapp/src/MyClass.php');
or
include('../src/MyClass.php');
PHPunit - best practices for testing user error states (not exceptions)
Mocking might be the right solution, the PHPUnit manual describes mocking as such:
The practice of replacing an object with a test double that verifies
expectations, for instance asserting that a method has been called, is
referred to as mocking.
So you could observe that wm_warn
was called, but it is not executed (nothing goes to the log).
You can read more about Simple Mocking under the PHPUnit Test Doubles topic that shows an example of this (where the above quote is also discussed). One problem you might face is that you can mock objects but not straight functions - so depends on the crustiness of your app :)
What order are phpunit tests executed?
One of the big benefits of DI is that you can avoid issues like this, by injecting a mock $settings
object that behaves the way you want. This gives you a true unit test of Widget without worrying about the implementation details of Settings:
$mockSettings = $this->createMock(Settings::class);
$mockSettings->method('someMethod')->willReturn('something');
$widget = new Widget($mockSettings);
// assertions here
Grouping phpunit tests. Launching test from specified directory
Found a quite easy way, even with config editing.
phpunit.xml
See: The XML Configuration File Phpunit Docs and Organizing Tests Phpunit Docs<
<testsuite name="Service">
<directory>tests/IntegrationTests/Service</directory>
</testsuite>
Command line:php ./vendor/bin/phpunit --testdox --testsuite Service
Related Topics
How to Discover Rss Feeds for a Given Url
PHP If Single or Double Equals
Checking If an Instance's Class Implements an Interface
How to Only Use Created_At in Laravel
Try/Catch Block in PHP Not Catching Exception
How to Convert the Time from Am/Pm to 24 Hour Format in PHP
Parse Timezone Int to String (Timezone Name) (Facebook API)
How to Expire PHP Session If User Is Inactive for 15 Mins
Instagram Login Programmatically
Permission Denied Despite Appropriate Permissions Using PHP
Changing the Add to Cart Button Text in Woocommerce for Items with Variations
Group Subarrays by One Column, Make Comma-Separated Values from Other Column Within Groups
JSON_Encode/JSON_Decode - Returns Stdclass Instead of Array in PHP
Package PHP5 Have No Installation Candidate (Ubuntu 16.04)
How Is Annotation Useful in PHP