Follow-up to #2056607: /core/phpunit.xml.dist only finds top-level contrib modules
Plus ça change, plus ça meme bug.
Problem/Motivation
Make a contrib module in ./module
, give it PHPUnit tests, type ./vendor/bin/phpunit -c core/
, and you're happy.
Put that same contrib module in a subdirectory, such as ./module/folder/my_module/
, and its tests vanish from PHPUnit's consciousness.
This is a problem because run-tests.sh finds and runs tests for the module, if you specify --module or --directory or just the module's group. This means the behaviors of these two test runners are out of sync, since phpunit won't find that module.
Proposed resolution
See comment #90 for the thought process: #2499239-90: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests
Since we can't simply specify a configuration using directory and ignore, we have to write our own suite loaders.
Extend \PHPUnit_Framework_TestSuite
with suite loaders for our different testing suites. Implement ::suite()
which is a factory method used by PHPUnit to allow us to generate the suite.
Suite loaders should use existing test discovery methods in order to more closely harmonize with the behavior of TestDiscovery and run-tests.sh.
Configure phpunit.xml.dist to use these suite loaders for our suites.
How to review this patch:
Install the Examples project. Try to get phpunit to discover the tests for phpunit_example
. Like this:
$ cd root/of/drupal
$ drush dl examples
// Examples is downloaded to modules/
$ ./vendor/bin/phpunit -c core/ --list-groups
// See phpunit_example listed.
$ ./vendor/bin/phpunit -c core/ --group phpunit_example
// phpunit runs phpunit_example tests.
Remaining tasks
- Inject root into the
drupal_phpunit_*()
functions present inbootstrap.php
so that we can be sure they're using the same root directory as the suite loaders. - Write tests.
User interface changes
Using the phpunit
tool to run tests is now more similar to run-tests.sh
when it comes to specifying --group
for all modules.
API changes
Related Issues
Comment | File | Size | Author |
---|---|---|---|
#120 | 2499239_120.patch | 3.56 KB | Mile23 |
#114 | 2499239_114.patch | 14.85 KB | Mile23 |
#114 | interdiff.txt | 7.79 KB | Mile23 |
#107 | interdiff.txt | 3.11 KB | Mile23 |
#107 | 2499239_107.patch | 9.2 KB | Mile23 |
Comments
Comment #1
Mile23Comment #2
Mile23This patch changes the scope of search for the 'unit' test suite.
Being too specific in this search means that contrib is highly constrained in its use of unit tests.
Comment #3
socketwench CreditAttribution: socketwench as a volunteer commentedUgh. This would skip any and all submodules tests, wouldn't it?
Comment #4
webchickComment #5
Kazanir CreditAttribution: Kazanir commentedAs I understand it, the previous issue's change broke all submodule tests by changing the module PhpUnit include path to a single layer. Instead it should be possible to revert that change but preserve the previous fix by using a **, like so:
However, I'm reading the documentation at https://phpunit.de/manual/current/en/appendixes.configuration.html and it looks like we should actually be using the <filter> element (rather than the <testsuite> definition?) to exclude files from code coverage reporting. The relevant piece of the docs appears to be here.
If adding tests for this is hard (and from the IRC discussion it appears to be at least non-trivial) then let's at least fix this in the most right way possible and comment the XML extensively to prevent this in the future.
Comment #6
jhedstromThe change to
phpunit.xml.dist
went in as part of #2232861: Create BrowserTestBase for web-testing on top of Mink, which had the added bonus of accidentally fixing #2203747: Current PHPUnit configuration scans into contrib vendor dirs. PHPUnit whac-a-mole.Comment #7
Kazanir CreditAttribution: Kazanir commentedOh I was confused. I think Mile23 linked the wrong issue by accident; jhedstrom is right and the diff of the testsuite refactoring is here.
Unfortunately, I'm not sure we can solve both of these problems with a static .xml.dist because PHP's glob() function turns out to not support the globstar technique I suggested above. (And it looks like PHPUnit's File_Iterator doesn't do anything other than invoking PHP's glob() to resolve wildcards.)
Comment #8
cilefen CreditAttribution: cilefen commentedThat is scary.
Comment #9
Kazanir CreditAttribution: Kazanir commentedYeah, I dug into PHPUnit's File_Iterator some more and it looks like it's really hard to achieve all of our desires here. I also noticed that the changes in #2232861: Create BrowserTestBase for web-testing on top of Mink also had a lot of other side effects, and they exclude a lot of tests in core modules, e.g.
./modules/views/src/Tests/
Is no longer being picked up. I'm not sure if that's intentional or not...but I suspect not?
Anyway, the problem here is that there is no support for pattern matching in PHPUnit's file iterator. It can match prefixes and suffixes, and it can exclude entire directories, but it doesn't support anything like globstar (the ** match-any-number-of-directories operator) or pattern matching for including and excluding files. So we cannot say, e.g., "include everything in both modules directories but then exclude anything containing the word vendor." This also seems to make it impossible to get nested module tests and support the split between test suites envisioned in the BrowserTestBase issue.
Comment #10
Mile23Behold Drupal.
No one uses multisite. The reason we have it is so we can RUN TESTS. :-)
Comment #11
cilefen CreditAttribution: cilefen commented@Mile23 Ehem?
Comment #12
XanoI've been thinking about this for a while, and I don't think I've ever seen a project of which the PHPUnit configuration allows for testing the projects dependent's. I've been shipping
phpunit.xml.dist
with all my Drupal 8 contrib modules. What's against doing that for all of contrib? Doing so means we won't enforce any directory structure limitations on contrib, which is what any solution to this issue would have to do.Comment #13
alexpottOne other problem to consider is how does the contrib module get tests on drupal.org.
I was wondering if we could dynamically generate the testsuites somehow and do our own test discovery in the process.
Comment #14
alexpottIn many ways I think the solution that @Xano suggests is the right way to go.
OTOH maybe we could add contrib tests using a listener. Something akin to the last comment on https://stackoverflow.com/questions/9535078/phpunit-different-bootstrap-...
Comment #15
alexpottSpent some time playing with the idea in #14. it doesn't really work because the list of tests is worked before you can hook into it and add more :( And although it is possible to add tests it breaks filtering and limiting the run to a specific test suite.
Comment #16
alexpottAfter spending a couple of hours looking at this if we want automated tests to work on d.o as things stand the best thing will be to only allow PHPUnit tests on the project level for contrib. That way we can keep the functional and unit split. The other solution is to remove the functional and unit split for contrib and do something like...
However test discovery performance will suck as contrib grows because all code will have to checked to see if it is a phpunit class.
Comment #17
Crell CreditAttribution: Crell at Palantir.net commentedalexpott: I'm not clear what you're suggesting. Are you suggesting that we make each contrib module have its own phpunit.xml.dist file, and it's on its own, and thus will not get run by phpunit in the site root? (I'd be OK with that.) Is that what you mean by "project level"?
Comment #18
twistor CreditAttribution: twistor commentedI agree with @Xano as well. Each module should ship its own phpunit.xml.dist.
As to how we run the tests on D.O. Why wouldn't
cd $module_dir && phpunit
work?Comment #19
alexpottBy project level I mean we don't support PHPUnit tests in sub modules. But actually even what we have now won;t work if some does modules/contrib/devel and modules/custom/my_custom_module.
So yep I think we should just stop trying to run everything with phpunit.xml.dist and change how run_tests.sh/Simpletest runs PHPUnit.
Comment #20
Crell CreditAttribution: Crell at Palantir.net commentedI am far more concerned with /modules/custom/my_custom_module than with /modules/mymodule/submodule, personally.
Er. I'm already using phpunit as the runner for all of my PHPUnit-based tests, and encouraging others to do so. That's what incoming PHP devs will be expecting. Why do we want to have a wrapper runner for PHPUnit when it's already got one?
Comment #21
Mile23Because Drupal, maaaan.
But seriously.
There are two design directions to come from here: 1) Drupal's contrib layout is wrong for PHPUnit. (This is my view.) 2) We should DrupalWTF-ify contrib modules and make our own test runner and so forth. (@Xano in #12, @sun in other issues... Me at other times...)
So.
Consider that we could demand that contrib goes into
modules/
(andthemes/
andprofiles/
) if you expect to be found by PHPUnit. And then we'd be done. We could also do this for SimpleTest, but that's a whole other conversation.The testbot can just always put contrib in those places for D8. (Last I checked, the
run-tests.sh
usesdist.phpunit.xml
as well.)Call this the 'break
sites/
' solution.As I made unclear in #10, the reason we care about
sites/
is because of multisite, which I'd say isn't widely used, but which we must keep so we can have SimpleTest. So therefore, toss it from the realm of test discovery while keeping it in the realm of functionality.Comment #22
Mile23And here's a patch for it.
This finds unit tests in
modules/examples/phpunit_example/
for instance.Comment #23
twistor CreditAttribution: twistor commentedAn attempt to clarify what I was saying in #18.
Why don't we just remove the ability to run unit tests from Simpletest/run-tests.sh and add another step in testbot that executes phpunit tests for the appropriate project? That way core's phpunit.xml.dist wouldn't have to be concerned with contrib at all. We could even get clever and point to a default phpunit.xml file if the module doesn't have one, so we can provide listeners and a bootstrap file.
While working on Feeds for D8, I've never gone to core/ and executed the tests from there using a --group flag or whatever. I just stay in my module directory and run phpunit.
Comment #24
Crell CreditAttribution: Crell at Palantir.net commented#23 is more what I'm thinking.
cd modules/mydir/somewhere/over/the/rainbow && phpunit
Done. Module devs can do that, and trivially get their tests filtered to just their module. If they have many submodules... well, it's their phpunit.xml.dist file, do what they want.
For testbot, when testing a module, it does the same thing. drush dl cd modules/rainbow && phpunit. kthx
All we lose in this case is "RUN ALL THE TESTS ON ALL THE MODULES ALL AT THE ONCE!" Which, given how long they take to run, I am unlikely to ever want to do anyway. (And once a site is actually built, let's be honest, many core tests don't work anyway due to form alters and theming.)
I think we're over-engineering this question.
Comment #25
Mile23#23:
Because that would mean we're removing something useful because we can't figure out configuration. :-)
#21 and #23 both simplify the issue and that's good. Either solution is a change, and either solution will require change to expectation and workflow.
With #21 we don't lose the ability to run everything at once, we're always using the same version of phpunit (cd into your module and unless you type '../../..etc../phpunit' you're using a different executable), we can say
phpunit --group my_module
, and we don't have the DrupalWTF of requiring you to cd into your module to run tests.Basically a policy that says 'You have to put your modules in a certain place in order to automatically run unit tests,' is less disruptive and more flexible than 'You can't run unit tests automatically at all.'
The architectural issue has been an ongoing conversation... I just found this one: #2209283: Remove /sites/all in favor of top-level directories
Comment #26
Crell CreditAttribution: Crell at Palantir.net commentedI disagree that's a Drupal WTF. I can't say I've ever run all of the tests in my vendor libs in a Composer-based project, ever. All of my own tests, yes, but not all tests of all libs I'm depending on. That seems like the Drupalism.
A policy of "you have to put your modules in X location for tests to work" means "you have to put your modules in X location, period". Unless each module has its own phpunit.xml.dist file, you can't actually run that module's tests at all if you don't. Which means /modules/custom/ is right out, not an option. That feels more problematic to me.
Comment #27
rreiss CreditAttribution: rreiss commentedSo what is the solution for those who using the modules/custom and modules/contrib structure? is it a bad practice now on Drupal 8? (I guess not..)
What about the submodules structure? how do I make PHPUnit find our custom tests?
As for #18 - I guess that we don't want to run our custom tests in separate from the rest of the tests,
I think that phpunit.xml.dist in each contrib / custom module is a nightmare..
Why not adding the best practices to the core's phpunit.xml.dist file? (e.g. ../modules/*/*/tests/src/Unit)
Comment #28
pfrenssen#23 matches what is typically done in the wider PHP world. I'm in favor for that.
What would be the use case of running PHPUnit tests of core + all contributed modules + all custom modules at the same time?
In a typical development workflow all third party code is considered to be "trusted". If you are working on a site that has custom code on top of Drupal core and a bunch of contrib modules you would only run the tests of your own custom developed modules. You trust that the tests of Drupal core + the contrib modules are green.
It's not practical otherwise, because it would imply you have to take responsibility for all test failures in third party code.
Comment #29
Novitsh CreditAttribution: Novitsh as a volunteer commentedFrom my point of view, you should only cover your own work with tests. If everybody does this, what a great world it would be :)
I agree on #23 and #28.
However, is this is the outcome, it needs to be documented very well.
Can this be closed?
Comment #30
jhedstromI think this is major, since it hides tests, and folks don't have a clear path on how to run those. I'm fine with the approach outlined in #23.
Comment #31
MixologicThe testbot needs to run all phpunit tests for a *project* not a module for contrib tests, so, at the very least this needs to be able to run all of the unit tests defined in a module, as well as all of its included submodules.
Also including a reference to adding the ability to have run-tests.sh *not* run phpunit tests so that we can use that for simpletest, and use phpunit's runner for the testbots, as it is, somewhat related.
Comment #32
Mile23Even if we used PHPUnit's runner for those tests, it still won't find them because this is a configuration issue with PHPUnit.
Comment #33
Mile23Patch in #22 still applies.
Note that we call PHPUnit differently, since vendor is now in the root directory.
Comment #34
Crell CreditAttribution: Crell as a volunteer commentedMixologic: If we had a policy of "core's phpunit.xml doesn't touch contrib; for your module, here's a template phpunit.xml file to use but otherwise it's on you to make sure it works properly, like if you're doing submodules or something" would that be workable from an infra standpoint?
Put another way, testbot need only download a package, cd into the package, and run "phpunit". It's the module developer's job to make sure that finds all tests (just as it is for any Composer package developer that's using Travis or Circle CI or whatever).
Side note: We really need to start discouraging multi-module packages. They made sense ten years ago, but no longer. We should actively discourage them now and try to remove support for them in D9.
Comment #35
Mile23So again, I'm not sure why it's a horrible idea to have PHPUnit scan all directories where there might be a module, to the extent that we have to plan to disallow submodules in Drupal 9 in 5 years.
Comment #36
MixologicYes, that would be ideal. We just currently have several multi module packages that already have an expectation that their tests will be run, so we either need to communicate to those devs that the current interface (automatically discovering their tests by file scanning) is changing, to be replaced with another method (they explicitly provide the phpunit.xml), or support both for some period of time. From an infra standpoint, we do not want to have to provide an automated testing UI for each individual submodule in a project.
- That makes a lot of sense from a "php packages" standpoint, but many of the multimodule packages are mini platforms in and of themselves (commerce/ubercart) Or they provide a base functionality, and a menu of modules that dont work without that base, but are not all necessary for every site (i.e. xml_sitemap). Im not familiar enough with the architecture of d8, and if all the current use cases for "submoduling" can be easly accomplished. Perhaps its in the change records somewhere, but maybe a blog post about "dont do submodules, do x in d8 now that we have autoloading + a service container" or whatever that actually works out to being.
Comment #37
jhedstromThis, coupled with the idea that a CI environment only has the current module + it's composer.json file, would lead to an old issue wherein vendor PHPUnit tests were being run, and throwing fatal errors (unless vendor were explicitly excluded).
Another issue with
is that contrib would not be able to depend on Drupal's
UnitTestBase
, or any of the new PHPUnit-based test classes (Kernel and Browser), unless Drupal was declared as a dev dependency in each and every composer.json.Comment #38
Mile23So we have two sets of user needs:
1) I want to run PHPUnit in one place from the command line for all my testing needs. This is nice-to-have.
2) The testbot wants to be able to just execute one script and be done, for both core and contrib, running all submodules' tests. This isn't essential for core, but it's pretty close to essential for contrib, and it also means not developing a ton of special cases within the testbot runner. That is, if you're making a D8 contrib module you assume it runs under the D8 environment, so that's what the testbot gives you.
There are a few ways to satisfy the testbot:
1) Say no. You can't run submodule tests. ::crosses arms::
2) Add scanning for all submodules to
dist.phpunit.xml
.3) Have another
phpunit.xml
file somewhere, named something likecontrib.phpunit.xml
. This then becomes the only special case the testbot has to deal with.Option 3 also has the side effect of letting me say
./vendor/bin/phpunit -c contrib.phpunit.xml --group myModule
.The downside of option 3 is that we then have to maintain parity between it and
dist.phpunit.xml
, and work some magic on simpletest so it knows about it. No free lunch. Sigh.Comment #39
MixologicOne other caveat: I dont think that the testbot will necessarily get away from needing to run phpunit tests via run-tests.sh. The phpunit command line runner does *not* handle fatal errors in tests gracefully. The wrapper of run-tests.sh around php-unit's testrunner allows us to execute each test in an isolated php process, and more importantly, handles the case where the test has some sort of syntax error or causes a core dump in php. If you run phpunit with --process-isolation, it runs all the tests, but the failed tests produce no output other than the php fatal error/segfault to stderr. Essentially those tests act like they've been skipped - there is no test artifacts like xml. In order to track which tests fail like that so we can provide a complete list of passed and failed tests (regardless of reason), we have to have a wrapper around phpunit that either looks at the stderr/stdout of the phpunit runner, or creates a fail record that is replaced with whatever the real output of phpunit is (like we're doing now).
So, keep that in mind when we talk about testbot requirements. Maybe this issue doesnt affect testbots at all.
Comment #40
tim.plunkettComment #41
Crell CreditAttribution: Crell as a volunteer commentedHm, good point. Making Drupal itself a dev dependency sounds like a terrible idea. :-) I don't know if it's at all feasible to make a drupal-testing package that is just our testing extensions. Honestly that would be a good thing for us to do anyway, just for general code cleanliness, but I don't know if that's possible during RC, or what else it would break.
Comment #42
dawehner@Mixologic Did you considered open up an upstream issue for that?
Comment #43
Mixologic@dawehner I had thoughts about it, and that would be ideal. --process-isolation should still produce results per test.
Comment #44
Mile23I still need to run PHPUnit tests on submodules on the testbot. This is for Examples, specifically
phpunit_example
.The testbot uses
run-tests.sh
, which defers tophpunit.xml.dist
for configuration of PHPUnit test suites.Thus I need to change
phpunit.xml.dist
if I want the testbot to run tests which teach people how to write tests.I think it's fair to say that if you want submodule PHPUnit tests, you have to place the module in the top-level
modules/
directory.We limit submodule tests in
sites/
based on specific needs having to do with not searching throughsites/default/files
and so forth, since that would be a security issue.A wide search through
modules/
does not run into this problem.If a module in
modules/
has its ownvendor/
directory, then it is Doing Things Wrong. Its composer-based dependencies should live in the rootvendor/
directory, having been managed by a composer-based build process.Also, we have added a composer-based build step to core which removes test directories from
vendor/
, as a security and space-saving effort.With all that in mind, here's a patch.
It widens the net on the search through the
modules/
directory, and documents why. It also documents that we exclude submodules undersites/
on purpose.Comment #45
Mile23Derp. Editor was set to convert to tabs.
Also:
phpunit.xml.dist
still excludescore/vendor
, which is no longer present. Fixed here to excludevendor/
.And why not just exclude
modules/*/vendor
? Done here.Comment #46
jhedstromWild card exclusion didn't used to be supported in PHPUnit--does this work now?
Comment #47
Mile23Well, check right above the patched part... We're including
../sites/*/modules/*/tests/src/Unit
.I just verified this by adding a module under
modules/
and putting a failing test in itsvendor/
directory. Altering the phpunit.xml.dist file to remove the exclusion, it finds the test. Adding it back, it doesn't.Comment #48
MixologicThe testbots/drupalci do not use phpunit.xml.dist when trying to determine which tests to run. It uses run-tests.sh --directory, which recurses from the root level of the module looking for php files inside of /tests/ and /Tests/ directories.
See: https://www.drupal.org/commitlog/commit/2/a42acf99652f5c82bf937e067acf73...
Comment #49
Mile23Thanks, @mixologic.
OK, so that leaves us with the testbot finding files that I can't run locally, leading to a false positive locally.
From the issue for the patch above (#2551981: Add --directory option to run-tests.sh test discovery) @jthorson says:
Absolutely it should be the responsibility of the test framework, not the runner. But it shouldn't be up to
run-tests.sh
to ignore PHPUnit configuration, for all the reasons that have led to so many objections in this issue.So, just to tally, we have:
1) SimpleTest in the UI which *does* discover and run the submodule PHPUnit tests, according to.... policy?
2)
run-tests.sh
which usesphpunit.xml.dist
for test configuration such as strict rules (I think) but which scans for tests everywhere, depending on calling mode.Which leaves us with:
3) PHPUnit at the command line, which honors whitelist/blacklist from
phpunit.xml.dist
, meaning it *can't* run tests that the others *can*, for... reasons.Sadly, the only useful information you'll get about PHPUnit tests comes from running phpunit at the console, which means you're SOL if you want to find out why a test failed. Here's what
run-tests.sh
tells you when you pass--verbose
:So. because all of this is true, someone should RTBC my patch so I can get to work on stuff, please, because apparently the testbot doesn't care.
Comment #50
jhedstromConfirmed that with the patch in #45 I cannot reproduce the fatal error from #2182231: Testing scans for tests in contrib vendor directory. So sometime in the past year, PHPUnit started supporting wildcard exclusions.
This is tagged as needs tests, but aside from actually adding some placeholder test modules to the
/modules
directory, I'm not sure what we can do here.Comment #51
jhedstromDoes the patch need updating to include Kernel and Functional tests in addition to Unit?
Comment #52
Mile23We can read in phpunit.xml.dist and then parse it and then compare that to what it should be.
Which is stupid.
And seems like a waste of time because none of the testing systems are paying attention to the whitelist or blacklist, so it's a useless file read that no policy cares about.
Which is why I did this: #2607866: [meta] DrupalCI/SimpletestUI/run-tests.sh/phpunit not in harmony
Comment #53
neclimdulThis has problems when using base classes or traits in the contrib module but otherwise seems to work. I'm guessing we have to tweak something in core/tests/bootstrap.inc as well.
Note: This issue will also cause problems for people using drush make or just drush to install modules as it installs modules in modules/contrib and modules/custom causing there to be no top level modules at all.
Comment #54
Mile23Rerunning test in #45.
Comment #55
Mile23Autoloading traits or base classes is definitely the responsibility of
bootstrap.php
.phpunit.xml.dist
should only care about which tests get run.Setting back to needs review because I'm not sure what else has to be done. We should file a follow-up if
core/tests/bootstrap.php
isn't finding some classes or traits.Comment #56
jhedstromStill wondering about my question in #51 regarding the other tests (kernel + browser) now specified in
phpunit.xml
.Comment #57
dawehner@jhedstrom Under the assumption that doing this patch is right, we should adapt it for kernel and browser tests, true.
Comment #58
Mile23No assumption about it: It's the right thing to do. :-)
This patch changes the kernel and functional test suites to match the unit one.
Comment #59
jhedstromDoes this need to be repeated for browser and kernel tests?
Comment #60
jhedstromComment #61
XanoWhy are we still doing this after the drawbacks have been mentioned in #12 already? When I want to test core, I do not want contrib to be tested as well. It's also a bad idea to assume core has any knowledge of how to run contrib tests. We're needlessly painting ourselves in a corner here.
Comment #62
dawehnerI have to agree with xano, phpunit.xml files should test stuff of a package, not an entire checkout.
Comment #63
Mile23Here's @Xano #12:
That is the problem this issue solves.
You can easily use the Drupal-installed PHPUnit to go to an arbitrary directory depth within
modules/
.Within
sites/
, a number of issues arise (listed above) for arbitrary depth, so we disallow that. Not a perfect solution, but then what is?Core makes all kinds of assumptions about how to test contrib. In fact, if you look at https://www.drupal.org/node/2499239#comment-10520662 you'll see that the config we're arguing over right now is completely irrelevant to the testbot, or to run-tests.sh.
You are the first to say: I don't want to run contrib during core dev, to which I say: Yay! Someone else uses phpunit instead of run-tests.sh! But also: The behavior you desire (don't run contrib when I'm working on core) is accidental; phpunit.xml.dist makes an effort to run contrib tests, but fails for arbitrary directory depth.
So shall we make a separate test suite for contrib? That would be in contradiction to the needs of others above who want to organize tests by type, not by domain.
I want: The same version of phpunit that core has (so running from DRUPAL_ROOT) , and to be able to run tests filtered by group or testsuite. That way I have the best chance of mimicking the testbot without actually using it.
Comment #64
XanoMaking assumptions is what got us in trouble in the first place.
The behavior I desire is that we stop doing things we should not be doing. We develop Drupal core, so we must test Drupal core. Testing contributed modules is not our responsibility here.
If someone wants to test their entire application, let them find their own solution. Or maybe we can provide a solution for that, but let's not force everybody to use that solution, especially when we do that in a way that is different from what the rest of the world does.
TLDR;
phpunit.xml.dist
is for package-level testing. Let's keep it that way.Comment #65
jhedstromAs mentioned in #37 and #41 we still need a way to allow contrib to readily use core's base test classes (unit, kernel, functional/browser). As it stands now, using core's
phpunit.xml
allows this (if contrib module tests are discoverable).Comment #66
neclimdul2 quick observations. I've read #12 a couple times and it seems the complaint there is the enforced directory structure. While its a sane structure its not actually enforcing it since this isn't how testbot or anything else work.
But it is decidedly not. That's not the way its written and this issue is about fixing/improving and existing feature not adding a new one. You can run tests and I have run tests on contrib and I've repeatedly watched as the devel tests break during the 8.0.0 development because they weren't up to date.
Comment #67
XanoAll that requires, is running PHPUnit with
--bootstrap ./core/tests/bootstrap.php
.None of the comments actually address the fact that we shouldn't test contributed modules when we actually want to test Drupal core. Having to figure out how to filter tests actually makes the whole workflow more complicated.
Comment #68
XanoIt is how most of the PHP world uses
phpunit.xml.dist
. Let's stick to that convention. There's no reason to deviate here. We're trying to get off our island, not dig ourselves in. This also means anyone wanting to test core can just test core.If we also want to provide application-level testing, we should ship with an additional application-level configuration file for PHPUnit. I'm still not entirely sure what the use cases for that are, but I'm not against including this functionality, as long as it does not interfere with more basic and common functionality.
Comment #69
Mile23The point here is that we need to replicate the testbot as much as possible while using
phpunit
at the command line, which for me means using the executable invendor/
, and a Drupal-supplied config file.So if you want to avoid core when running contrib and vice-versa, then as @neclimdul points out, that's a feature request. We could maybe do it with a
contrib.phpunit.xml
file, though that didn't get any traction when I mentioned it above in #38.Comment #70
XanoWhat exactly do we need to replicate, and why?
It's not a feature. It's fixing buggy behavior by removing wrong assumptions and replacing them with dedicated, targeted solutions.
Let's put it this way: if someone creates a suite of modules that has no
modules
subdirectory, top-level directories per module, then this patch just breaks because it makes the wrong assumptions.Comment #71
Mile23I want to run unit tests with PHPUnit. I want to generate coverage reports with PHPUnit. I want to do code quality stuff with PHPUnit. I want to automate this process. I don't want a lot of special cases to deal with during contrib development. I want my tests to be run on the same PHPUnit executable that they'll run with on the testbot. I want the same strictness level as the testbot.
Also, currently, #2578721: [examples] Drupal Examples for Developers is somewhat blocked by having only this hobbled PHPUnit config file to work with.
Right, so if you run the test using the patched config and expect it to work within a module directory instead of DRUPAL_ROOT, then yes, you have problems. But it's the same problem as if you expected that without the patch.
So, as you say, you'd add
--bootstrap 'path/to/boostrap.php'
to get autoloading, and maybe add your ownphpunit.xml
within the module directory. Which is what you're already doing.Not sure how that's a problem, because it's what you seem to want.
This patch fixes the behavior of finding all the submodules in
DRUPAL_ROOT/modules/
, so if you decide to change your ways, and run phpunit fromDRUPAL_ROOT
, put your suite of modules inDRUPAL_ROOT/modules
, and use--group
to filter for your submodule, it will actually work, instead of not finding your tests.Comment #72
Crell CreditAttribution: Crell as a volunteer commentedSounds like we need at least semi-formal user stories here, frankly, because we're all talking about different things. At which point this issue should get retitled/resummarized, since it becomes about more than the stated bug.
Comment #73
Mixologic+1 to that.
The best way to achieve this is to get the testbots to execute unit tests separately from simpletests, so that the testbots run the PHPUnit executable.
That is currently blocked on the fact that the PHPUnit executable is an inadequate tool from a CI perspective. It needs to be able to gracefully handle segfaults when running in isolated process mode. Graceful handling of segfaults means that if a test aborts, we get a structured response that tells us which test failed. If we can fix that upstream, we can get the testbot to use phpunit the same way as people using it on the commandline use it.
Comment #74
dawehnerDid you created an issue on phpunit itself about that?
Comment #75
MixologicI have not. run-tests.sh already does what we need from a testbot perspective, and I do not have any spare cycles to try and improve phpunit upstream.
Comment #76
Mile23#2638290: Create stream_wrapper_example can't happen because of this issue.
PHPUnit can't find either of the two tests that example defines.
The testbot finds both tests.
run-tests.sh can only find the Kernel test when I give it the group name. But when I tell it the class name of the Unit test, it discovers the test and runs it.
Drupal 8 UI only finds the one test and fails it, even though it's passing.
There is no way I can feel alright about these tests under this circumstance.
See: #2607866: [meta] DrupalCI/SimpletestUI/run-tests.sh/phpunit not in harmony
Comment #77
Mile23Modified the issue summary because any other problems seem to have been solved.
There is no down-side.
Patch in #58, updated to reflect #59, which also fixes existing problems with those test suites. (Excluding
./vendor
instead of../vendor
)Once again: The *ONLY* change this makes is to allow the PHPUnit command line tool to find unit tests in sub-modules under
DRUPAL_ROOT/modules
.It continues to be strict about submodule test discovery under
sites/
, which is a security concern (because we'd be scanning user-supplied content for tests).The reason we'd be doing this is to allow anyone to type
$ ./vendor/bin/phpunit --group modulename
and test their module without setting up a whole CI process and one-off test runner, though those would of course still be an option.Work continues on improving PHPUnit-based test results in the UI and
run-tests.sh
to provide even *more* useful options: #2427191: Test-runner ignores @group annotations for PHPUnit-based tests in modules. #2608532: Simpletest UI munges PHPUnit-based test output #2607866: [meta] DrupalCI/SimpletestUI/run-tests.sh/phpunit not in harmonyComment #78
wiifmAfter battling with Drupal 8 and PHPunit, and quite frankly extremely misleading documentation for several hours, I stumble across http://drupal.stackexchange.com/questions/162719/phpunit-with-custom-con... which in turns leads here.
In my particular scenario, I had my custom modules under a directory
/modules/custom/my_module
.Before patch (stock Drupal 8.0.4) - my PHPunit tests are not recognised, I nearly go crazy.
After patch #77 - my PHPunit tests are magically found, I rejoice.
This is a huge +1 from me for RTBC, this will improve the DX a lot, and reduce worldwide developer frustration measurably.
Comment #79
Xano#2677122: Allow ./core/phpunit.xmldist to run package tests only addresses the concerns raised in #12.
Comment #80
alexpottAlso over in #2329453: Ignore front end vendor folders to improve directory search performance we've been discussing optimisations to directory search when module have javascript assets which can lead to thousands of files in a module. With the fix here we're going to be scanning them in order to test.
Comment #81
neclimdulI'm not sure I understand the connection to the npm/bower issue.
Comment #82
alexpott@neclimdul phpunit is going to have to scan all of those directories if we make this change.
Comment #83
Mile23It already scans those directories for top-level modules.
That's one reason why #2301873-8: Use ExtensionDiscovery to register test namespaces for running PHPUnit might be a good idea.
Comment #84
bojanz CreditAttribution: bojanz at Centarro commentedI agree with Xano, I have no expectation for Core's phpunit.xml to discover contrib tests.
All my scripts point phpunit to the exact contrib folder (such as modules/commerce), which in turn discovers any submodule unit tests.
That said, #77 fixes the existing behavior under the existing assumptions, so I'm fine with that getting in while we converge on the better idea.
Comment #86
alexpottNo, HEAD does not scan outside of a module's tests directory.
The currently implementation also completely breaks the testsuite implementation meaning that phpunit --testsuite Functional will run contrib module's Unit, Kernel and FunctionaJavascript too...
Comment #87
alexpottThis doesn't need changing it can just be removed - it'll never be found anyway.
Comment #88
Mile23HEAD does this:
...note lower-case f... :-)
We also still see this:
Comment #89
alexpott@Mile23 sorry I messed up the casing of my command but that was hardly the point. The point it that with this change trying to limit by test suite won't work for contrib modules.
Comment #90
Mile23Oh, I thought you were referring to HEAD not working, because you said 'current implementation.'
Gottit.
PHPUnit here just grabs all the test files it can find based on the testsuite's
directory
andexclude
rules. We ignore those rules inTestDiscovery
which Simpletest and UI use, which means it's easy for them to drift apart.bootstrap.php sets up the autoloader to be able to find tests in contrib mapping like this:
$namespaces['Drupal\\Tests\\' . $extension . '\\'][] = $dir . '/tests/src';
This is again different fromTestDiscovery
which (currently) silos different test types by namespace, for a different behavior in Simpletest and UI.TestDiscovery
finds tests in project subdirectories, whereasphpunit.xml.dist
does not.This leads us back to #2605664-123: [Needs change record updates] Align TestDiscovery and bootstrap.php's non-permissive loading of PSR-4 namespaces for traits where @alexpott lays out principles for design in Drupal tests. The basic desire is to have all the different test tools run the same tests under similar circumstances. So running
phpunit --group modulename
should run the same tests asrun-tests.sh --module modulename
andrun-tests.sh modulename
.So: Is
TestDiscovery
right, or isbootstrap.php
+phpunit.xml.dist
?I'd say
TestDiscovery
is correct, and we should find tests in arbitrary modules because that's howrun-tests.sh
works, and that's our main testing standard for Drupal 8.So to do that in
phpunit.xml.dist
, we have to re-write the config to search in arbitrary directories undermodule/
but notsites/
for security reasons.This brings the
phpunit
tool config into harmony withTestDiscovery
, but creates the problem from #86: When the user specifies a testsuite, we lack a way to express which types of test to run at an arbitrary directory depth, so the command will run all the tests.So now we're outside the realm of test suites we can configure through XML if we want phpunit's --group argument to behave like run-test.sh's --module.
This leaves us with some options:
It turns out we can look to CakePHP for an example of a suite loader for tests with hard dependencies: https://github.com/cakephp/cakephp/blob/master/phpunit.xml.dist The "Database Test Suite" specifies a test suite by PHP file rather than directory, which is some code that runs to determine which tests belong to the suite.
The suite runner is here: https://github.com/cakephp/cakephp/blob/master/tests/TestCase/DatabaseSu... which implements
suite()
.suite()
adds tests to the suite based on whatever logic is desired, and then PHPUnit runs them.So:
Is this some work I should get to? OK, since you said yes.
Comment #91
alexpottSo myabe we use TestDiscovery in phpunit.xml.dist - that'd actually be super helpful.
Comment #92
Mile23It should go the other way around. We want no dependencies in our suite loaders, because we're using them to test the dependencies we would have.
Also, I hijacked this issue #2301873: Use ExtensionDiscovery to register test namespaces for running PHPUnit which was originally about using TestDiscovery. It's from the days before all the different test suites, however, so it's pretty well stale.
Comment #93
Mile23OK, so here we go.
This is a proof-of-concept which only deals with the unit test suite.
It does use
TestDiscovery
's static methods, which is adequate for now. The problem is thatTestDiscovery
is in theDrupal\simpletest
namespace, so whether it's autoloadable during test time is up for grabs, given recent conversations. At the moment it works, however. Also those static methods are static for a reason and could easily be refactored to some other location such as these suite loaders.This patch adds
UnitTestSuite
, which implements thesuite()
method, which magically gives it the power to define a test suite for PHPUnit. It performs some file scanning on places in core and contrib known to have unit tests. PHPUnit then takes the classes we give it and does further checking with reflection to make sure everything's cool and to determine@group
s and so forth.UnitTestSuite
is outside the realm of autoloadable classes, in keeping with the minimal-autoload footprint approach. It could easily move somewhere more autoloadable, because: When you callUnitTestSuite::suite()
you'll be supplied with an iterator of all available unit tests. :-) This has to come in handy for other testing systems.After the change,
--testsuite unit
runs the same number of tests as before. And it passes the examples test, namely it finds@group phpunit_example
. :-)Running without any group our testsuite argument results in the same number of tests before and after.
If this doesn't look scary and awful to people I'll optimize a bit and move on to the other suites.
Comment #94
alexpott@Mile23++ nice work. This will make everyone's life easier.
Autoloading from \Drupal\simpletest\src is fine - the bit of autoloading I question is - \Drupal\simpletest\tests\src - because if you enable autoloading from there you are changing the run-time behaviour as that is not normally available. The first location can be autoloaded from the moment the module is installed. Also given the relative locations of all the files are known - we can just do an include if we decide to not allow this to be autoloaded at this point.
It'd be nice to do it first without an API change. We can do the root passing around later. Yes it aids testability - but let's do that in a followup. Because atm its still quite hard-coded.
Comment #95
Mile23The module is never installed in this scenario, except when running a test that installs it. But
bootstrap.php
loads the non-test namespace for all extensions, so we're good I guess. If we decide to change this policy, we can move/refactorTestDiscovery
.Round 2.
No longer injecting
$root
into the bootstrap functions. I'd argue that there's no way functions in bootstrap could be considered API, but yah.Each of the suite classes is very similar. One might say they look copy-pasted. :-) However each discovery method is just different enough that it would be more complex to normalize them into a single function with a calling signature, etc. Also, needs may change later on and it will be easier to know what to change this way. So some naive code there is probably best.
Each suite class has a protected helper method like
getUnitTests()
. This was to allow for testing, but since we're not injecting$root
, that will come later.Comment #96
Mile23Forgot an interdiff...
Also just to mention that the thought process here is in #90. #2499239-90: Use test suite classes to discover different test types under phpunit, allow contrib harmony with run-tests
Comment #97
neclimdulWith the exception of the Unit tests, we can actually narrow this down to one method they can share similar to cake. DRY! :)
Comment #98
neclimdulI really like this patch BTW, makes maintaining phpunit.xml much easier and provides a really handy tool for site builders building their own test suites.
I don't think it does anything for #2605664: [Needs change record updates] Align TestDiscovery and bootstrap.php's non-permissive loading of PSR-4 namespaces for traits or anything for TestDiscovery at all but that's fine with me. :)
edit: fix issue number
Comment #99
neclimdulFor anyone coming along and wondering what these have to do with anything like I did, phpunit skipped adding tests that didn't have any test methods at all but the way we're explicitly adding them these get run. This causes new failures in the phpunit runner. That is a distinct change in the discovery ATM.
Comment #100
jhedstromI manually tested this approach and it finds tests in the root modules directory as expected.
Updating the IS to reflect how to manually test (now that vendor isn't in the core directory).
I love this simplification of the xml file!
I'm guessing these changes to abstract are because the new approach would 'discover' these classes? One question though, why doesn't our old discovery also find them?
Comment #101
jhedstromcross post there, thanks for the clarification on the change to abstract.
Comment #102
Mile23Ossum. :-)
It's static. I looked it up: https://www.drupal.org/coding-standards/docs#types :-)
This wouldn't be a @todo except @alexpott said to do it in a follow-up. Can we leave the @todo?
This isn't adding a namespace. It's adding files to the test suite.
Comment #103
jhedstromI think new
@todo
s are only allowed if they link to an issue.Comment #104
neclimdulHah, I didn't mean to take over the patch, just was breaking it down the patch to understand it and tossed the changes back up. :)
1) That is ridiculous and in direct contradiction to PSR-5's types. Maybe its just vague... WTF? static and self have 2 distinct meanings so that seriously needs to be fixed. ATM it doesn't matter so rather then fight it just have the return type not match the documented type I changed the instantiation too... seriously WTF though?
2) Sure, I missed Alex's comment. Like jhedstrom said it should get added back with the link, feel free to do so.
3) err... yeah I think I just grabbed cake's method to start with and replaced some words and never read it. That's quite odd for sure.
Comment #105
dawehnerI really like the elegance of that solution ...
Nitpick: These comments are wrong.
Random fact: 4 months ago we had 14k tests, now we have 18k tests? This is a bit odd
Comment #106
Mile23Setting NR to run testbot.
Comment #107
Mile23Addressing #105 and a few others I found.
Follow-ups:
drupal_phpunit_*()
functions.Comment #108
Mile23Updating issue summary.
Comment #109
MixologicThats due to http://cgit.drupalcode.org/drupal/tree/core/tests/Drupal/Tests/Component...
For some strange reason the test author imagined that we really needed to test every single one of these possibilities. Fortunately they're super fast and didnt increase testing load too much, but yeah..
Comment #110
Mile23Since this is a bug report and it only improves tests I'm setting to 8.1.x. The patch applies to either branch. Re-running the test on #107.
Comment #111
Mile23Patch still applies to 8.1.x.
As per #94 the patch in #107 still doesn't inject root into the bootstrap functions, which precludes testing and is kind of iffy.
Comment #112
alexpottHow about adding a test which adds a class which extends TestSuiteBase and allows you to specific the root to test? That way we get the important test coverage of the logic in TestSuiteBase:: addTestsBySuiteNamespace().
Comment #113
alexpottAlso rather than breaking BC we could just make $root optional - so continue with the current behaviour if it's not supply but also make testing easy.
Comment #114
Mile23Turns out it doesn't matter anyway for testing, because
drupal_phpunit_find_extension_directories()
usesSplFileInfo->getRealPath()
, which can't deal with stream-based filesystems. Thus we can't test using mocked file systems.So we can have the optional
$root
parameter todrupal_phpunit_contrib_extension_directory_roots()
, which won't change the API (if you say so...) but which guarantees that we're scanning the same directory as the test suite class. This makes me happy. :-)We have a test of the special case for core Unit tests, which is most relevant.
Room for improvement will be a test for namespace/path manipulation for extensions (minus the discovery part, for reasons given above). Which I'll do when I get to TCDrupal which I'm an hour late for right now because I was doing this instead. :-)
Comment #115
alexpottLike the test! You're right it is nice to have one :)
Well it is a permitted API change because we have a BC layer.
Could be fixed on commit.
Comment #116
dawehnerI really think this is simply beautiful! You know these moments when you nearly wanna cry for happiness? Its not exactly that, but its getter there.
I wasn't sure, but this test change seems really like a detail which is personal preference, so RTBC.
This is a bit confusing. We have a stub class but then still call out to protected methods via invokeArgs. What about improving the readability by making these methods public just for the test.
Comment #117
alexpott@dawehner I think I prefer your way too but the benefit of this far outweighs personal preferences.
Committed 492728a and pushed to 8.1.x and 8.2.x. Thanks!
Comment #120
Mile23Hehe....
I was still working. :-)
Rebased with the commit, patch adds tests.
Comment #123
Mile23Turned #120 into a follow-up: #2785025: Expand testing for TestSuiteBaseTest
Comment #125
klausiCreated #2794715: TestSuiteBaseTest cannot be executed as standalone test as a follow-up.
Unfortunately these TestSuite classes are not suitable to run with Paratest, so I created a script to generate a phpunit.xml file for parallel testing on Travis CI: https://github.com/klausi/drupal/blob/8.3.x-phpunit-travis/travis/testru...
Comment #126
Mile23This issue now has a change record, which it should have had all along: https://www.drupal.org/node/2799437
Comment #127
Mile23Finally getting back to the caveat about TestDiscovery being in \Drupal\simpletest from #93: #2863055: Move TestDiscovery out of simpletest module, minimize dependencies