Colocating tests and production code is the default in some programming languages and has become the de facto standard in others. I have to admit that while I liked it in Go and TypeScript, I always felt a little queasy about doing it in PHP. I couldn't really name it; it was more a sense of "it doesn't feel right". Of course, it turned out to be just a bullshit bias, because it works just as well in PHP as it does in other languages. No matter which language, the benefits are the same:
- You see at first glance if there are tests for a piece of code
- You don't have to navigate as much anymore
- You don't have to maintain parallel structures in source and test directories
Let's give it a try, shall we?
Migrating a simple project
Take this setup with test and production code in parallel structures and a single test suite:
src/
└── MyProject
├── MyClass.php
└── MyOtherClass.php
tests/
└── MyProject
├── MyClassTest.php
└── MyOtherClassTest.php
<testsuites>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
</testsuites>
The good news is that migration is as easy as dragging and dropping the MyProject
directory from tests
to src
; at least if you're using PhpStorm. PhpStorm will take care of changing namespaces appropriately. If your editor of choice doesn't offer this kind of refactoring, search and replace will do as well.
src/
└── MyProject
├── MyClass.php
├── MyClassTest.php
├── MyOtherClass.php
└── MyOtherClassTest.php
The last thing you need to do is point PHPUnit to the new location. Just change the directory from tests
to src
and you're ready to go:
<testsuites>
<testsuite name="default">
<directory>src</directory>
</testsuite>
</testsuites>
What about multiple test suites?
When there is more than a single test suite, things get a bit tricky. Let's take a look at this setup:
tests/
├── Integration
│ └── MyProject
│ └── MyClassTest.php
└── Unit
└── MyProject
└── MyOtherClassTest.php
<testsuites>
<testsuite name="integration">
<directory>tests/Integration</directory>
</testsuite>
<testsuite name="unit">
<directory>tests/Unit</directory>
</testsuite>
</testsuites>
If we colocate them the same way we did in the first example, there's no way to determine which test belongs to which test suite. Here are two options to approach this problem.
Using suffixes
The first option is to change the suffix Test
to either IntegrationTest
or UnitTest
. This allows a clear distinction to which suite a test belongs, although it's a bit clunky.
src
└── MyProject
├── MyClass.php
├── MyClassIntegrationTest.php
├── MyOtherClass.php
└── MyOtherClassUnitTest.php
<testsuites>
<testsuite name="integration">
<directory suffix="IntegrationTest.php">src</directory>
</testsuite>
<testsuite name="unit">
<directory suffix="UnitTest.php">src</directory>
</testsuite>
</testsuites>
Using groups
The second options is to use the group annotation to categorize tests. Since groups and test suites are different things and a test suite in PHPUnit cannot be composed of groups, the configuration can be simplified to a single test suite. Just remember that you have to use the group
option instead of the testsuite
option when running tests (phpunit --group integration
or phpunit --group unit
).
/**
* ...
* @group integration
*/
final class MyClassTest extends TestCase
{
// ...
}
/**
* ...
* @group unit
*/
final class MyOtherClassTest extends TestCase
{
// ...
}
<testsuites>
<testsuite name="default">
<directory>src</directory>
</testsuite>
</testsuites>
Tweaking PhpStorm
I was a little disappointed when I discovered that the files in my project window looked like this:
The test classes have the red/green arrow indicating a test, but I was hoping they would be highlighted as I was used to with jest tests. Fortunately, this is easy to customize.
Go to Preferences | Appearance & Behavior | Scopes and set up a new scope matching test classes (file:*Test.php
):
Then add an element for the new scope and pick a color under Preferences | Appearance & Behavior | File Colors:
It should look something like this now:
Sweet!
Another thing you may want to tweak is the severity of certain inspections. I noticed warnings showing up that weren't there before. This is probably due to the way PhpStorm handles files based on what type of directory they're in (Preferences | Directories). Since we already have our new scope set up, we just have to change the severity under Preferences | Editor | Inspections to mute unwanted inspections.
That's it
That's all I have to say on this topic at the moment. I've been using this approach in my personal projects for over a year now, and introduced it at work about a month ago. So far, everyone likes it.
Top comments (0)