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

Issue fork drupal-3586832

Command icon 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:

Comments

godotislate created an issue. See original summary.

godotislate’s picture

Status: Active » Needs review
godotislate’s picture

Issue summary: View changes
Issue tags: +Needs CR

This 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.

joachim’s picture

Which 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.

catch’s picture

This 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.

godotislate’s picture

I'm fairly certain I've seen test modules that define one-off routes for a single test. I'll look for an example.

godotislate’s picture

Here's one: the basic_auth_test module. Its only use AFAICT is to provide routes for Drupal\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.

godotislate’s picture

I know there are a lot of test routes inside test modules, but those are often useful in other circumstances

I 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.

joachim’s picture

> 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.

godotislate’s picture

Issue tags: -Needs CR

Since this has missed 11.4, added CR https://www.drupal.org/node/3605544.

smustgrave’s picture

Should the existing test you mentioned in #8 be converted?

godotislate’s picture

Per #6, I'm thinking no, a conversion or a batch of conversions can be do in their own issues.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Needs Review Queue Initiative

10-4

I ran the test-only locally since I can't run the job anymore (thank you gitlab)

Behat\Mink\Exception\ExpectationException: Current response status code is 500, but 200 expected.
/var/www/html/vendor/behat/mink/src/WebAssert.php:888
/var/www/html/vendor/behat/mink/src/WebAssert.php:145
/var/www/html/core/tests/Drupal/KernelTests/KernelTestHttpRequestTest.php:111


Test code or tested code printed unexpected output: Uncaught PHP Exception Error: "Call to private method Drupal\Core\Routing\AttributeRouteDiscovery::createRouteCollection() from scope Drupal\KernelTests\KernelTestAttributeRouteDiscovery" at /var/www/html/core/tests/Drupal/KernelTests/KernelTestAttributeRouteDiscovery.php line 22

MR is small and don't see any open threads. No objections.

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new91 bytes

The 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.

godotislate’s picture

Issue summary: View changes
Status: Needs work » Needs review

Rebased 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.

nitinkumar_7’s picture

Nice 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.

godotislate’s picture

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).

That 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.

godotislate’s picture

Changed the test class property to have a random string value to make it clear that the route markup is solely controlled by the property.

dcam’s picture

Status: Needs review » Reviewed & tested by the community

I 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 is BinaryFileResponseTestController. 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.

catch’s picture

Version: main » 11.x-dev
Status: Reviewed & tested by the community » Fixed

This 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!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • catch committed 06b9973e on 11.x
    feat: #3586832 Allow kernel test classes to define their own routes
    
    By...

  • catch committed f44b16fb on main
    feat: #3586832 Allow kernel test classes to define their own routes
    
    By...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.