Problem/Motivation
#3311365: Use PHP attributes for route discovery makes it possible to use the Symfony Route attribute applied to Controller classes and their methods to define routes.
#3390193: Add a drupalGet() method to KernelTestBase makes it possible to make HTTP requests in kernel tests using drupalGet().
With these two things in mind, it would be useful for kernel test classes to be able to define their own routes. Often functional tests are used to check content in a controller, and often such tests also make use of database storage to set and retrieve values between the test method and the controller. In certain cases, this could be simplified if the controller method was in the kernel class itself, and values could be set and retrieved between test method and controller method via class properites
Steps to reproduce
Proposed resolution
In KernelTestBase, register an event subscriber (extend AttributeRouteDiscovery) that discovers routes from the attributes in the kernel test class
Remaining tasks
Add change record (or add to https://www.drupal.org/node/3324758 if in before 11.4).
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|
Issue fork drupal-3586832
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3586832-allow-kernel-test
changes, plain diff MR !15571
Comments
Comment #3
godotislateMR is ready: https://git.drupalcode.org/project/drupal/-/merge_requests/15571
Comment #4
godotislateThis will need a CR, but going to leave that open for now, because if we get this in for 11.4, it's probably better that we add to https://www.drupal.org/node/3324758.
Comment #5
joachim commentedWhich routes are you thinking would benefit from this?
I know there are a lot of test routes inside test modules, but those are often useful in other circumstances -- e.g. I've been reusing some of them for the drupalGet() in core conversions.
Comment #6
catchThis is a great idea - would be good to have an example of an existing test we can convert - not in this issue because we're still working through kernel http request things but for an example of the sort of boilerplate we should be able to remove.
Comment #7
godotislateI'm fairly certain I've seen test modules that define one-off routes for a single test. I'll look for an example.
Comment #8
godotislateHere's one: the
basic_auth_testmodule. Its only use AFAICT is to provide routes forDrupal\Tests\basic_auth\Functional\BasicAuthTest::testControllerNotCalledBeforeAuth()that aren't used anywhere else. I haven't checked the whole test class to see if all the methods could be converted to a Kernel Test, but that one method can at least.Comment #9
godotislateI haven't completely thought this through, but one possible way to be able use the same route across multiple kernel tests would be to put the
#[Route]method in a trait.Comment #10
joachim commented> but one possible way to be able use the same route across multiple kernel tests would be to put the #[Route] method in a trait.
True.
Though one drawback of this occurs to me -- if the route is defined in a kernel test, then you can't ever test that route manually. That's sometimes useful when developing or debugging tests.
Comment #11
godotislateSince this has missed 11.4, added CR https://www.drupal.org/node/3605544.
Comment #12
smustgrave commentedShould the existing test you mentioned in #8 be converted?
Comment #13
godotislatePer #6, I'm thinking no, a conversion or a batch of conversions can be do in their own issues.
Comment #14
smustgrave commented10-4
I ran the test-only locally since I can't run the job anymore (thank you gitlab)
MR is small and don't see any open threads. No objections.
Comment #15
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #16
godotislateRebased AttributeRouteDiscovery after merge conflict from #3584793: Use PHP attributes for form route discovery. Did a small refactor as well to pass the kernel test class name in to the new Discovery class, instead of the kernel test class object.
Comment #17
nitinkumar_7 commentedNice work on the rebase -- the private -> protected fix on AttributeRouteDiscovery and the string-based $kernelTestClass constructor look right, and that null check in collectRoutes() is a good safety net.
One nitpick on the "remove unnecessary loop" commit: that loop wasn't just noise it hit the route twice with two different testMarkup values, which is really the whole point here (proving the route reflects live state from the test class, not just that it resolves). Collapsing to one string loses that. Genuinely curious why it got cut though, might be missing context. Mind adding back a second check with a different value? Doesn't need the loop, just two assertions.
Rest of the chnages looks solid, no other concerns.
Comment #18
godotislateThat change was from my original work on this issue and well before this rebase. I think it's unnecessary to show that the value be changed. The idea is to show that a property from the kernel class can be used to set the content for an endpoint defined in the class, and the test as it is now does that. Showing the value can change is not as necessary, and, while maybe it's microoptimization, it saves a few CI cycles skipping it.
Comment #19
godotislateChanged the test class property to have a random string value to make it clear that the route markup is solely controlled by the property.
Comment #20
dcam commentedI don't have anything to add with regard to a code review. It looks OK to me.
To test this issue I attempted a route conversion of my own by embedding a test route in its test. The first route that I found that's used by a Kernel test is
binary_file_response_test.download, whose test isBinaryFileResponseTestController. I copied the controller function to the test class, added the Route attribute, and then deleted the entire test module. The test module only contained the one route. The test continued to pass after performing the conversion. So that's exciting. We can eliminate that test module after this issue lands.This one looks good to me.
Comment #21
catchThis looks great. Would not have imagined it would be approx ~8 lines of actual code.
Like @joachim I sometimes manually install test modules and visit routes to debug things, but I don't think I've needed to do that for a kernel test route, only for ones in functional js/functional tests. Also with a kernel test it's easier to debug the entire code flow because it's in a single process. I think the DX of being able to see the route logic in the same place that it's tested outweighs this anyway, as well the convenience when writing tests.
Committed/pushed to main and 11.x, thanks!