PHPUnit configuration (phpunit.xml) -- loading in a bootstrap? - php

Situation
We're using PHPUnit in our project and are using a phpunit.xml to ensure things like backupGlobals is turned off.
To further ensure the include path is set and autoloading is active, we also cascade our test bootstraps. That is to say, every test and alltests-suite has a require_once(__DIR__ . '/../bootstrap.php'); at the top, all the way up to the base folder level, where it obviously reads require_once(__DIR__ . '/bootstrap.php');, and the actual bootstrap file resides.
Essentially, our tests are autonomous. You can call any AllTests.php in any folder and any *Test.php by itself and they'll run with the right configuration.
Except no. 'Wait a moment.'
That's only true if we either force our developers to use phpunit --configuration=path/to/phpunit.xml or they're in the folder with the phpunit.xml (so that PHPUnit pulls it out of the current working directory when it is executed).
Occasionally, this makes it incredibly hard to determine why tests on one developer's machine are breaking and why they're running on another. It just takes forgetting that the bootstrap is not the only thing we need to have the same test environment. Keep in mind that since you couldn't forget the bootstrap if you tried, because it's in the tests themselves, forgetting other settings, especially usually-optional ones like that (if you're in the folder with the phpunit.xml, it's automatically pulled), is easy.
In fact - it's happened a few times.
Question
Is there a way I can supply which phpunit.xml to use in the test file being run, such as in our conveniently ubiquitous bootstrap file, rather than supplying it to PHPUnit beforehand, be that by command-line switch or by being in its directory ?
A cursory glance at the code suggests the answer is no - configuration well and truly seems to be loaded before test files are even pulled:
[PHPUnit/TextUI/Command.php]
...
if (isset($this->arguments['configuration'])) {
$configuration = PHPUnit_Util_Configuration::getInstance(
$this->arguments['configuration']
);
$phpunit = $configuration->getPHPUnitConfiguration();
...
That does make some sense, given that the configuration can contain test white- or blacklists.
Really, it wouldn't make sense to load test filters in the test bootstrap itself, so that's half the potential configuration out the window with, but the actual behavioural flags of PHPUnit...
[sample of part of our phpunit.xml]
<phpunit
backupGlobals="false"
backupStaticAttributes="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
syntaxCheck="false"
processIsolation="false"
colors="true">
...perhaps with the exception of 'colors' strikes me as something that the test itself should be able to decide on some level.
Consolation prize for...
Admittedly, right now I'd be happy just knowing if I can teach PHPUnit backupGlobals="false" from the bootstrap file, if someone knows of a way.
(If fruitless, the practical answer I'll pursue will probably be to copy the phpunit.xml into all subfolders. I'd like to avoid that solution since it creates redundant copies, and if we ever choose to change a setting... yeah, ouch!)

Direct answer: No, you cant do that.
Longer story - this kind of problem is better solved by changing the habits of developers.
Here is we do it:
All developers always run the tests from the tests root directory, which has the sole phpunit.xml with all the necessary configuration - including bootstrap, which sets up an class autoloader.
We dont have testsuites as such, tests are grouped using directories, no AllTests.php anywhere, because its not necessary. PHPUnit can take a name of directory and run all tests inside it.
Its still possible to run any single test by giving a path to it or whole testsuite (by giving path to the directory). It just has to be done from the tests root directory all the time or it wont work.
Doing it like this means giving up the freedom of starting PHPUnit from any directory, but to be honest - I dont feel like thats a loss at all.
The gains are much bigger: the amount of housekeeping code is reduced, developers cannot forget anything and results are therefore consistent.

My solution is to add a bash function
function phpu ()
{
phpunit --colors --bootstrap ~/path/to/bootstrap.php "$#";
}
Then add this to all dev .bashrc files and they can switch to using that.
We like to call them from vim so I had to add this to .vimrc: set shellcmdflag=-ic
Then you add nmap ;t :! phpu % to run the test file you're currently in.

You could update the start script (Windows bat file, or shell on *nix) and have logic in there to configure to the location of the phpunit.xml. If it is in the current directory, use it, otherwise point to the main one.
I also agree with Anti though, that all the tests should always be run, as you want to ensure that the changes, even in a directory branch, do not affect other code. Therefore, always run from the top of the tree. This also requires that the test execute quickly, but I have not really had a problem with PHPUnit on that.
Maintaining the PHPUnit.xml in each directory would be a maintenance nightmare, unless is was symbolically linked from other paths to ensure there was only one actual file.

Related

PHPUnit - suffix argument ignored in directory tag under testsuite

I recently realized that I cannot run all my phpunit tests at once unless I use the "php artisan test", and I don't understand why. I would use the other option but I can't do coverage with that.
I assume it's somehow related to my configuration file being wrong because of a syntax change, or my directory being set wrong or something, because I can't find anything else.
The suite is found, but the suffix is not applied, whether I write it as ".php" or "Tests.php" or anything else. I know, because if I rename all my test files with Test at the end instead of Tests, then they suddently get recognized by phpunit. I also tried all numbers of directory options, like "./tests/Unit" and "./tests/Unit/*" but to no avail.
I would really appreciate some help if anyone has any idea what is happening, because it's driving me crazy. It took me a day to figure out I needed to rename my files just to make it work, and at this point I don't even want to do that unless I absolutely must, since there is this suffix feature.
I just want to understand why it does not work, and how to use it properly, in hopes of being able to learn how to use phpunit in the right way.
I can see two potential causes:
1) Your phpunit.xml may not be configured properly.
Try to change the "testsuite" and "coverage" section as shown below:
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./app</directory>
</include>
</coverage>
2) Make sure all your tests methods use the #test annotation in the method’s DocBlock:
/** #test */
public function your_test_method_name()
{
...
}
See: https://phpunit.readthedocs.io/en/9.5/annotations.html
As an alternative to prefixing your test method names with test, you can use the #test annotation in a method’s DocBlock to mark it as a test method.
If you don't include the docblock, make sure your test method names start with "test":
public function test_method_name()
{
...
}
See: https://phpunit.readthedocs.io/en/9.5/writing-tests-for-phpunit.html
The tests are public methods that are named test*.
Nothing worked to fix the suffix. I tried reinstalling xdebug, php, and phpstorm. I ended up just renaming the files as I said. Only working solution I could find.
I know, because if I rename all my test files with Test at the end instead of Tests, then they suddenly get recognized by phpunit.
That sounds like the default configuration for phpunit and could be a sign that your configuration file you're trying to control the behaviour with is not effective. That is, however you run Phpunit, it is without the configuration file and the path only, e.g.:
/path/to/phpunit -- /path/to/test/directory
Phpunit then will work with the default configuration - but it must find it in the working directory. So either the configuration files have a name different to the names that phpunit uses for auto-detecting or they are not in the working directory, phpunit will start without loading a configuration file and just execute what it can find in the directory with the default suffix "Test.php".
(In Phpstorm the working directory can vary when invoking Phpunit, even between runs of the same action, but that just as a comment.)
What you can do in Phpstorm: Configure the Phpunit configuration file to load. A good way to do that is to specify a default Phpunit configuration file in Settings -> PHP -> Test Frameworks in the Test Runner Section for Default configuration file.
Another thing is to specify the working directory explicitly in either the run action you already created which does not do what you want or by modifying the Phpunit run action template. Here a screenshot for the template:
A way to check the effectiveness is to make use of a bootstrap file in configuration and make it explode. Then you know whether or not something was effective.
Also PHpunit normally writes out the name of the configuration file if one is in use.

How do I set the working directory for phpunit using the XML file?

I'm looking through the documentation, but I'm not seeing any option to change the working directory used when running tests.
I'm using PhpUnit as it's included in Laravel. I want to be able to run vendor/bin/phpunit from my project's root directory, and have it run using the /public directory as the working directory.
I tried running ../vendor/bin/phpunit from the /public, but since the phpunit.xml file isn't in the public directory and I don't want to specify my config file path every time, that won't work.
Is there something I can add to my phpunit.xml file to tell it to run tests using the /public directory as the "cwd" (current working directory)?
Based on the feedback I received in the comments and the documentation, I determined the following:
It's probably not possible to change the cwd that phpunit uses by default (well, it's possible in PhpStorm, but not the command line without writing some kind of wrapper script)
Code that depends on being run from a specific directory is not a good idea.
What I had was some code in one of my classes like this:
$var = file_get_contents("../some_file.json");
This works fine -- until you try to add unit tests. The web server runs using the /public directory as the cwd, while phpunit will run using the root directory.
Rather than trying to force phpunit to always use a particular cwd (/public), I decided it's probably best to remove relative paths from the code that rely on a consistent cwd. So the line above becomes:
$var = file_get_contents(base_path("some_file.json"));
I didn't want to change production code that was already working just to get some tests in place, but this change seemed insignificant enough. (and it's an improvement anyway)
Well, you'd have to do the actual chdir in PHP, but you can define a bootstrap script in the XML (<phpunit bootstrap="./bootstrap.php">) and have that change the working directory.
Alternatively, you can put a setUpBeforeClass function into your test class that changes the working directory.

What is .phpunit.result.cache

When I run tests with PhpUnit on a new package I'm creating for Laravel, it generates the file .phpunit.result.cache.
What to do with that? Do I add it to my .gitignore file or not?
I'm using PHPUnit 8.0.4
This file helps PHPUnit remember which tests previously failed, which can speed up your testing flow if you only re-run failed tests during development. This is useful for test-driven workflows in which you have configured tests to run automatically, such as on file save, and the same collection of tests is being run repeatedly.
It is also a good idea to add the cache file .phpunit.result.cache to
your .gitignore so that it does not end up being committed to your
repository.
https://laravel-news.com/tips-to-speed-up-phpunit-tests
If you would prefer not to generate the file then you can run phpunit with the --do-not-cache-result option, as pointed out by #Slack Undertow in the comments. This might be desired when running tests as part of a build pipeline, for example. Or, as #codekandis pointed out, the same option is available as the cacheResult attribute in phpunit.xml.
You can also change this file location by editing phpunit.xml:
<phpunit
...
cacheResultFile="../.temp/fs_cache/.phpunit.result.cache"
>
Or completely disable it by
<phpunit
...
cacheResult ="false"
>
Official PHPUnit explanation (I currently don't find any other useful official details).
This caching is required for certain other features to work.
You can disable it with:
<phpunit
...
cacheResult="false">

Configuring File Names for PHPUnit

I am a new user of PHPUnit, and I am converting our existing tests (asserts) into the PHPUnit framework to provide a better test environment and code coverage. However, I need to know how to try to get PHPUnit working with our testing code structure.
Our project directories are similar to the following:
Application1/
CREDIT_CARD.class - Class naming convention for CREDIT_CARD
CREDIT_CARD.class.test - Automated Tests for CREDIT_CARD.class
File.php - Application File
File.php.test - Automated tests for File.php
File2.php
File2.php.test - Automated tests for File2.php
Application2/
ANOTHER_CLASS.class
ANOTHER_CLASS.class.test
DifferentFile.php - Application File
DifferentFile.php.test - Automated tests for File.php
lib/
UTIL/
SHARED_CLASS.class
SHARED_CLASS.class.test
VISUAL/
VISUAL_TOOL.class
VISUAL_TOOL.class.test
I need to know how to configure the PHPUnit tests so I can run the tests in lib/UTIL/.test (which load the class file using the setUp() method) then the lib/VC/.test, followed (if successful) by the Application1 and Application2 tests. I saw mention of a PHPUnit_xml file and a bootstrap file, but I can not find a reference template to see if these are what I need. Any help would be appreciated.
I know the documentation refers to a test.php addition to the file names, but I am hoping to not have to change our structure and naming conventions as I would like to be able to run a mix of the files until they are all converted over to the PHPUnit framework. Changing names will cause a procedure change in our company and training for the developers, which I am trying to avoid.
Thanks in advance for any assistance.
So you have files named Foo.class.test instead of the PHPUnit default FooTest.php ?
That shouldn't be a big problem as PHPUnit allows you to configure what file suffixes are to be treated as "tests". The Test.php is just the default.
Have a look at the xml config part of the docs regard test organisation
What you want is:
<testsuites>
<testsuite name="Our new shiny phpunit test Suite">
<directory suffix=".test">./sourceFolder</directory>
</testsuite>
</testsuites>
PHPUnit will then scan through the source folder including all files that end in .class.test and leaving the other files as is.

Deployment with Capistrano

I am trying to get into PHP app deployment with Capistrano. I have two config files that I need to be "edited" depending on where I deploy it. It's basic stuff like database name and root url (Codeigniter). Can I make Capistrano edit specified automatically? Let's say I want to edit the following in the file /system/config/edit.php:
$test = '';
// edit to
$test = 'Hello World';
Thanks,
Max
What I generally do in this kind of situation (even though I don't use Capistrano) is to have several config files commited to source control.
For instance :
config.php for development machines
this file is the one that's always used by the application
config.testing.php
config.staging.php
config.production.php
And when deploying the application to the server, I just have to copy the file corresponding to the current environment to "config.php" -- as this one is the one that's always used by the application.
It means that I have to do a file copy during the build process, yes, but :
it means there is no need for any search and replace, that can break
it also means every config files are commited to SVN (or whatever source control software you are using)
If your configuration files become too complex, and duplicate lots of stuff, you can think about having one "default" config file, that's always included, and sub-config files that only define what depends on the environment.
What that, what I said before still stands : just include the "default" file at the begining of each other.
My Unix is knowledge isn't quite up to scratch so I can't quite get the syntax perfect for what you want. However, Capistrano allows you to directly use the Unix command-line by invoking :run_method within your configs.
The Capistrano code might look something like the following:
run "grep -R --files-with-matches '$test = "";' /system/config/ | xargs perl -pi~ -e 's/\$test = "";/$test = "Hello World";/'"
I would check up on that find and replace function working as expected before implementing it live though.
If you need any more help, I'd recommend checking out the Capistrano Handbook, it should answer most of your questions.

Categories