-
Notifications
You must be signed in to change notification settings - Fork 24.6k
[6.0] Add PHPUnit bootstrap file to allow execution of console commands before a test run #5050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@mfn as per your comment on my first PR - would love you to give this version a spin on your suite of 6,000+ tests. And to answer your question: There is no need to manually do any config cache / busting. This version caches to a dedicated |
|
I tried it and realized I'm on 5.8 (obviously), had to replace But unfortunately, as I thought, it's not measureable with my suite. I also can only run it in a timely manner on CI (in parallel) in the cloud and we all know they don't give you the prime resources for doing that. Sorry for bringing up hopes. |
|
Can't we move the listener to the foundation component in |
|
@driesvints I think that would probably be a good idea yea (and I mentioned this in my initial comment). I'll send the PR to |
|
The only bad thing with the Not too long ago I used a dedicated listener for a Laravel project and it would not have worked together with your because I too had to boot the framework they. Eventually I found a different solution for the same problem without using a listener. Do you think it's possible without that? |
|
@mfn could you shed light on why your listener would have conflicted with this one? This listener does boot the framework - but the thing that takes the longest during booting is collating all the config. This listener caches the config on first run, meaning that all subsequent boots are ~50% quicker (as per my benchmarking). I would think that means your listener would actually become faster because when it boots the framework it doesn't have to collate all the config on the fly. Or am I missing something? |
|
The Listener was, as in your case, a special one which also booted the framework. But: it just came to my mind, I'm not using it anymore and I think hardly anyone does so I guess you can just disregard my comment ;) |
|
@mfn no worries. if you do think of any specific scenarios where this could conflict (with or without another listener) please do mention it. I'd be happy to run some tests and see if we need to make adjustments if any are needed. |
|
I've just pushed a new commit to this. You can now specify arbitrary commands to run before the test suite, rather than locking it down to config caching only. It makes it more generic so devs can define the commands to run before the testsuite runs for the first time. <listeners>
<listener class="Illuminate\Foundation\Testing\Listener">
<arguments>
<!-- Define artisan commands to execute before the test suite runs -->
<string>config:cache</string>
<string>route:cache</string>
<string>event:cache</string>
<string>some-3rd-party-package:cache-stuff</string>
</arguments>
</listener>
</listeners> |
|
Isn't Listener a really gwneral name? I'd rather have a CacheForTestsListener or another name that describes what this is doing better |
|
Agreed. I think PreRunListener or SetupListener or something along those lines would be better. I’ve been more focused on the implementation than those details up until now so if anyone has any other suggestions, please do share. I also figured Taylor, if he wants this, would know what he’d want to call it as well. |
|
...although if this is the Laravel listener, I dont think it would be the worst thing to call it Listener. Just like the TestCase is called TestCase instead of BootsFrameworkTestCase. But I don’t have strong opinions on the naming either way TBH |
|
Someone just suggested this could be done in a bootstrap file...and I think I was too deep down the rabbit hole to realise that is probably the better way to go compared to a listener. Just ship a pretending these comments were not terrible...it could be something like this... <?php
use Illuminate\Contracts\Console\Kernel;
require_once __DIR__.'/../vendor/autoload.php';
/*
|--------------------------------------------------------------------------
| Bootstrap the testing environment
|--------------------------------------------------------------------------
|
| You have the option to specify console commands that will execute before your
| test suite is run. Caching config, routes, & events may improve performance
| and bring your testing environment closer to production.
|
*/
$commands = [
'config:cache',
'route:cache',
'event:cache',
];
$app = require __DIR__.'/../bootstrap/app.php';
$console = tap($app->make(Kernel::class))->bootstrap();
foreach ($commands as $command) {
$console->call($command);
} |
|
Yeah I think that makes decent sense probably. |
|
I've just pushed changes to implement to bootstrap file instead of the Listener. I've commented out the |
|
My two cents: I think the file would be better lived under I also believe the current |
|
We've actually found in our initial feature test benchmarks that caching the routes can actually slow down the test suite. Looks like it is due the the performance of Gonna do some more digging into this throughout the week and will report back regarding route caching. Happy to close this and re-open once I have a better overall understanding of if route caching is a worthwhile thing to include in the bootstrapping process. |
|
Now that I think about it, this would also break tests which register routes on runtime (I think that's a common approach for testing middleware |
|
@m1guelpf runtime defined routes continue to work if the routes are cached, e.g. the following test passes with routes cached before the test run: public function test_runtime_routing()
{
Route::get('my-test-route', function () {
return 'expected';
});
$response = $this->get('my-test-route');
$this->assertSame('expected', $response->content());
}Let me know if this isn't what you are referring to though. I might have misunderstood. |
|
@timacdonald can you move the bootstrap file to |
|
@adrianb93 what did you actually do in the file to achieve those results? |
Nothing crazy. They're both apps that work with many databases. It was just moving most of the setup work to the start of the suite.
In both cases, each test starts transactions in each test setup and rolls back in the before application destroy. These aren't the first apps I've worked on that have built up this sort of unavoidable DB cruft. The bootstrap file helped make things better for these apps. |
|
Really appreciate so many people taking the time, here and on other forums, to give me feedback on this PR ❤️ Hope it comes in handy for you and others. |
|
@timacdonald awesome job! Working great for me when running from the command line (+-2s faster), however having some issues getting it work when running individual test via PHPStorm interface. Anyone who has managed to get that working? |
Thanks for this @adrianb93 I'm on Laravel 5.8 and I was experimenting 419 errors after the tests. Edit: I just realised that env variables are correctly set when I dump them during tests but this is still
@patrickbrouwers I'm on Ubuntu with PHPStorm 192.5728.108 and run them through docker. For me, they still work individually with no extra configuration. |
|
@patrickbrouwers Same problem here, I can't run individual tests. I got this error : Same for you ? I'm investigating for a solution... |
|
@bastien-phi yes that's the error I'm getting too. I got it to work when I specify the absolute path in the env settings for the cache files. |
|
@patrickbrouwers I found the exact same solution right now ! |
|
@bastien-phi I personally always gitignore the phpunit.xml and ship a phpunit.xml.dist |
|
@patrickbrouwers I will just update $envVars = ['APP_CONFIG_CACHE', 'APP_SERVICES_CACHE', 'APP_PACKAGES_CACHE', 'APP_ROUTES_CACHE', 'APP_EVENTS_CACHE'];
foreach ($envVars as $envVar) {
$_ENV[$envVar] = __DIR__ . '/../' . $_ENV[$envVar];
}@timacdonald Any other idea as |
|
@cbaconnier as per my comment here: #5050 (comment) have you set the env vars correctly in the The env values need to be set differently in 5.8. Instead of - <server name="APP_CONFIG_CACHE" value="bootstrap/cache/config.phpunit.php"/>
+ <env name="APP_CONFIG_CACHE" value="bootstrap/cache/config.phpunit.php"/> |
|
@timacdonald Oh... 😅 Thanks! That was it |
|
@cbaconnier no worries, that has tripped a few people up. @bastien-phi not sure. Can you put your setup in here so we can try and work out why it isn't working for you? Are you calling it from inside PHPStorm as well? If so, could you try on the command line as well and see if it is any different? Would be good to work what part of your stack is causing issue for ya. |
|
@timacdonald I created a repo from a fresh install of laravel, with the phpunit bootstrap file : laravel-phpunit-bootstrap I updated the bootstrap file with a "patch" in order to make it work properly. Before the patch, Calling this command from the project root, the tests goes well. The difference between the two calls is that from inside PHPStorm, current working directory is the directory when the test file belongs (in that case If calling the command from another directory (let's say As the environment variables are relative paths, the application tries to find the directory The patch updates relative paths to absolute path and everything goes well. Unfortunately, this is far from being a clean fix... |
|
@bastien-phi I can confirm it works without the patch in If I am understanding that correctly, they are saying the CWD is always set to the project root.
Can you check that option out (and possibly whatever else is in there), confirm you don't have a value in there, and maybe have a play and see if there is anything in there you need to change to get it working? |
|
@timacdonald This option is kept empty and the CWD is not set to the project root but to the directory where the test class belongs : It is possible to explicitly set CWD as project root for phpunit in PHPStorm with Run Configuration Templates. Be sure to remove all existing configurations first. But IMO, there is still a problem because CWD has to be project root to run the tests. Before this feature, it was possible to launch tests from another location. The problem does not comes from the bootstrap file itself but from the fact that the files are defined with relative paths in the
1 is not a good idea at all ! <?php
use Illuminate\Contracts\Console\Kernel;
chdir(__DIR__.'/../');
require_once __DIR__.'/../vendor/autoload.php';
/*
|--------------------------------------------------------------------------
| Bootstrap the testing environment
|--------------------------------------------------------------------------
|
| You have the option to specify console commands that will execute before your
| test suite is run. Caching config, routes, & events may improve performance
| and bring your testing environment closer to production.
|
*/
$commands = [
'config:cache',
'event:cache',
// 'route:cache',
];
$app = require __DIR__.'/../bootstrap/app.php';
$console = tap($app->make(Kernel::class))->bootstrap();
foreach ($commands as $command) {
$console->call($command);
} |
|
Laravel 5.8 with 171 tests 5-10% improvement 👏️ Before: After: 🙂️👌️ |
|
nice @stevelacey! Thanks for sharing. Glad to see a little perf bonus there for ya |
|
@timacdonald I replaced your codes and it improved ~10% of test suit which is nice, But i dont know why i'm getting |
|
@foremtehan I just had this same problem, I suspect it’s because 5.8 doesn’t have the separate caches per env logic thats coming in 6.0, and thus Laravel sees cache and ignores the .env? And thus your DB_NAME is wrong, and thus no such table. @timacdonald is there something else we should patch in? |
|
@foremtehan @stevelacey see #5050 (comment) You will also want to run just once right now p.s. @foremtehan ~10% is a nice speed improvement 🎉 |
|
@timacdonald this PR is causing some issues: We'll need to figure out solutions for these fast or else it might be best to revert this PR. |
|
#5071 does the job |
|
@driesvints digging into this now. Will respond in the relevant threads to keep discussions together. |
|
I had exact problem, I just comment these out in |
|
@foremtehan that’ll mean that your testing env is being cached in your normal cache files, so you probably hit some weird bugs when using your app locally. Once laravel/framework#29890 is tagged it should be fixed and you’ll be right to restore everything |

This PR introduces a PHPUnit
watcherbootstrap file that caches the config before the test suite runs for the first time, which in my benchmarks reduces the boot time of the Kernel during a test run by ~50% without any additional work by the developer.This will only really have an impact on a larger test suite, but personally I think it is a worthwhile thing to consider.
The benchmark test suite ended up with the following durations:
Caching the config before a test run is good for a couple of reasons:
If, for example, you cache your routes in production but try deploy a sneaky closure route, your app is gonna blow up when you go to cache the routes during production. Having this in place will help you catch that kind of thing during testing, either locally or during CI. (you could catch this in CI if you are already calling this command before running the tests).
The PHPUnit
listenerbootstrap file will cache the config for the test environment before a test is run. Because it is in "User land" it is 100% opt-in. If you don't want it, don't use it. One handy thing about running the cache command in aPHPUnit listenerbootstrap file (as opposed to say in a composer script) is that it will already have the environment variables from the phpunit.xml file loaded, so we don't need to read those ourselves.Notes
As this doesn't require any changes to the framework, it is totally possible to just package up, which I'm more than happy to do.
The watcher could be kept in core with just the
phpunit.xmlchanges made here.it is possible we could see similar results by caching routes and events, but I haven’t tested this yet. I plan to extend the benchmark repo I setup to also have some feature (http) tests so we can see what kind of impact that might have on a test suite as well.
This PR is a simpler approach to my first run at this: laravel/framework#28998