Add drupalGet() to KernelTestBase

Created on 27 September 2023, about 1 year ago

Problem/Motivation

Kernel tests are significantly faster than browser tests (see ๐ŸŒฑ [META] Convert some tests into Kernel or Unit tests Active ).

Many browser tests only need to check something in the returned content of a controller. This can actually be done in a Kernel test with a few tweaks.

This would allow more tests to be converted to kernel tests.

Steps to reproduce

Proposed resolution

Add a drupalGet() method to KernelTestBase. This should output HTML files for debugging in the same way that BrowserTestBase does.

Working-but-in-need-of-clean-up code is here:

- Add code from https://github.com/mglaman/drupal-test-helpers/tree/main/src to KernelTestBase
- https://github.com/mglaman/drupal-test-helpers/commit/2dbed403a1872a3aea...

Code added to KernelTestBase should avoid duplicating code in BrowserTestBase -- this may require code being refactored from UiHelperTrait to a new trait.

Also to consider (for naming of traits, refactoring choices etc) is that as a follow-up, submitForm() should be made available to KernelTestBase as well.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

โœจ Feature request
Status

Active

Version

11.0 ๐Ÿ”ฅ

Component
PHPUnitย  โ†’

Last updated about 3 hours ago

Created by

๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @joachim
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Made a very rough start on a branch.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom catch
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States dww
    1. Huge +1. Love this idea.
    2. It'd be great to be able to use this to check the output from a View page display, too.

    Thanks!
    -Derek

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Got it working!

    The methods in BrowserHtmlDebugTrait aren't going to work for kernel tests -- they occasionally make assumptions about having Mink. So my approach has been to make a parallel KernelHtmlDebugTrait. It deviates very little, so as a follow-up we should look at merging them. Things to change include:

    - make the $message param of htmlOutput() required, not optional. There's only one place that htmlOutput() is called without it
    - changing how getHtmlOutputHeaders() works
    - fixing how $this->htmlOutputTestId is obtained
    - ... other things I may have forgotten about.

  • last update about 1 year ago
    Custom Commands Failed
  • Pipeline finished with Failed
    about 1 year ago
    Total: 203s
    #25284
  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia larowlan ๐Ÿ‡ฆ๐Ÿ‡บ๐Ÿ.au GMT+10

    Linking the original issue

    Rather than adding one method, writing a kernel based mink driver would be ideal

  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia larowlan ๐Ÿ‡ฆ๐Ÿ‡บ๐Ÿ.au GMT+10

    Copying from slack

    https://github.com/symfony/symfony/blob/6.4/src/Symfony/Bundle/Framework...

    Looks like symfony have a solution for this that uses sub processes

    If we can get this to work we get drupalGet, submitForm, assertSession etc all for free

    That was the original intent of step 3

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom catch

    ๐Ÿ“Œ Convert WebAssertTest to a Unit test Needs review is doing a similar-ish thing - except converting a functional test to a unit test where only very specific markup/headers need to be tested.

    Still think this is a great idea and there are probably dozens or hundreds of tests we could change from functional to kernel tests using this.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom catch
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia samit.310@gmail.com

    samit.310@gmail.com โ†’ made their first commit to this issueโ€™s fork.

  • Pipeline finished with Failed
    5 months ago
    Total: 71s
    #239181
  • Pipeline finished with Failed
    5 months ago
    Total: 118s
    #239183
  • Pipeline finished with Failed
    5 months ago
    Total: 115s
    #239192
  • Pipeline finished with Failed
    5 months ago
    Total: 837s
    #239206
  • Status changed to Needs work 5 months ago
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia samit.310@gmail.com

    Rerolled with 11.x branch, only one phpunit https://git.drupalcode.org/issue/drupal-3390193/-/jobs/2285879 issue is remaining.

    Thanks
    Samit K.

  • Pipeline finished with Failed
    5 months ago
    Total: 578s
    #241544
  • First commit to issue fork.
  • Pipeline finished with Failed
    5 months ago
    Total: 2120s
    #241558
  • Pipeline finished with Success
    5 months ago
    Total: 963s
    #241584
  • Pipeline finished with Running
    5 months ago
    #241624
  • Pipeline finished with Failed
    5 months ago
    Total: 617s
    #241672
  • Pipeline finished with Canceled
    5 months ago
    Total: 216s
    #241709
  • Pipeline finished with Failed
    5 months ago
    Total: 400s
    #241719
  • Pipeline finished with Success
    5 months ago
    #241723
  • pooja_sharma โ†’ changed the visibility of the branch 3390193-add-drupalget-to-kerneltestbase to hidden.

  • pooja_sharma โ†’ changed the visibility of the branch 3390193-add-drupalget-to-kerneltestbase to active.

  • Pipeline finished with Failed
    5 months ago
    Total: 294s
    #241792
  • Pipeline finished with Success
    5 months ago
    Total: 499s
    #241799
  • I have created separate MR 9043, removed the unrelated changes, observed pipeline test failures.

    Couple of things observed:
    Drupal\KernelTests\Core\DrupalKernel\DrupalKernelTest run successfully on local, it seems more of issue on here.

    Root cause of issue: Undefined global variable $base_url (core/tests/Drupal/Tests/KernelHtmlDebugTrait.php:130)

    If I tried to add empty condition & set the default value conditionally test passed (pipeline can be observed)

    I believe in below code of line, both of these variables , need to evaluate further whether these are set or not on giltlab as on local issue not replicate
    getenv('BROWSERTEST_OUTPUT_BASE_URL') ?: $GLOBALS['base_url']; (core/tests/Drupal/Tests/KernelHtmlDebugTrait.php:130)

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    @pooja_sharma What's the reason for the new MR?

  • As the existing MR in draft state & issue is not replicating on local it seems issue occurs due to existing MR is behind some commits.

    I don't have access to move the existing MR from draft state, already showing error message on existing MR: Merge request must not be draft, due to which can't even rebase from gitlab or also facing issue on local to rebase it.

    So I end up with raising new MR along with other changes mentioned in detail in prev comment.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Unless your work is a different approach to the existing MR, there's no need to create a new MR, it just adds confusion.

    The MR can be added to even with it in draft state - I can change it when it's ready for review, but IIRC

  • pooja_sharma โ†’ changed the visibility of the branch 3390193-kerneltestbase-add-drupalget to hidden.

  • Got your concerns, to avoid confusion I have set visibility hidden of new MR. & already added detail description for test failures.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom catch

    A good candidate for this change: ๐Ÿ“Œ Speed up ElementsTest Active .

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    @catch so work should continue here correct?

  • Pipeline finished with Success
    3 months ago
    Total: 433s
    #280936
  • Status changed to Needs review 3 months ago
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia samit.310@gmail.com

    Hello,

    Fixed the failing test cases.

    Thanks
    Samit K.

  • Pipeline finished with Success
    3 months ago
    Total: 416s
    #292447
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom longwave UK

    Discussed this at Drupalcon Barcelona in @joachim's BOF.

    In order to implement @larowlan's suggestion in #8 we researched what Symfony does and found that the framework bundle already provides a BrowserKit implementation that can talk to a Symfony Kernel: https://github.com/symfony/framework-bundle/blob/7.1/KernelBrowser.php

    It is hopefully possible to copy this class into Drupal core, wrap it in a BrowserKitDriver, and instantiate a Mink session from KernelTestBase - and we should be able to do almost anything that BTB can do in terms of calling drupalGet() and WebAssert assertions from KTB.

  • The Needs Review Queue Bot โ†’ tested this issue. It fails the Drupal core commit checks. 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.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Working on this.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Got the basics of this working!

    Getting @larowlan's suggestion to work was actually easier than we thought when discussed in the DrupalCon BoF -- all it needed was a 1-line method override! :D Symfony already has a Mink driver that operates the Symfony HTTP kernel.

    I found a simple functional test, NodeLinksTest, to convert as a trial, and very satisfyingly it's down from:

    > Time: 00:35.962, Memory: 12.00 MB

    to:

    > Time: 00:09.708, Memory: 12.00 MB

    However...

    HTML debug output isn't working yet. This is because KTB sets $this->siteDirectory to be in a VFS, and initBrowserOutputFile() doesn't know how to work that. (Also, KTB *really confusingly* sets $this->siteDirectory *twice!???). Not sure how to fix -- do we just chage initBrowserOutputFile() to handle that sort of path too?

    KTB has drupalGet(), but it doesn't yet have the ability to submit forms. I'd suggest leaving that to a follow-up, unless it turns out to be fairly simple.

    As ever, writing kernel test really confronts you with all the assumptions that code makes. Turns out node module has a hidden dependency on field module, for instance!

    We might want to as a follow-up allow installConfig() to only install specific config items, as for example in NodeLinksTest, I'm having to install field module just so installConfig() doesn't complain about not having a schema for field.storage.node.body, which we don't care about here.

    Tests that need to check for things like cache headers won't work with this, because AFAICT that's added later on after the HTTP kernel has returned a response.

    Tests that work with block output don't currently work with this, because there's no current theme. I've tried enabling stark and placing blocks, and that's not working. I switched to working on converting a test that didn't use blocks rather than go down that rabbithole. That's maybe something that can be figured out though -- maybe in a follow-up, unless it's simple.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    joachim โ†’ changed the visibility of the branch 3390193-add-drupalget-to-kerneltestbase to hidden.

  • Pipeline finished with Failed
    2 months ago
    Total: 166s
    #307611
  • The Needs Review Queue Bot โ†’ tested this issue. It fails the Drupal core commit checks. 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.

  • Pipeline finished with Failed
    2 months ago
    Total: 237s
    #307794
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Figured out the blocks issue.

  • Pipeline finished with Failed
    2 months ago
    Total: 107s
    #307817
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Why doesn't the response from the HTTP kernel have cache headers? Are those added later in a normal request? And how -- is there a way we can get them here too for tests that need them?

    I think we should add a helper to do this:

        $this->container->get('theme_installer')->install(['stark']);
        $this->container->get('theme.manager')->setActiveTheme(
          $this->container->get('theme.initialization')->initTheme('stark')
        );
    

    as it's pretty tedious boilerplate.

  • Pipeline finished with Failed
    2 months ago
    Total: 109s
    #307825
  • The Needs Review Queue Bot โ†’ tested this issue. It fails the Drupal core commit checks. 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.

  • Pipeline finished with Failed
    2 months ago
    Total: 143s
    #307859
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim
      Line   tests/Drupal/Tests/BrowserHtmlDebugTrait.php (in context of class      
             Drupal\KernelTests\KernelTestBase)                                     
    

    That seems like a false negative to me -- I can't change the signatures of methods in a trait that other classes are using too.

  • The Needs Review Queue Bot โ†’ tested this issue. It fails the Drupal core commit checks. 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.

  • Pipeline finished with Failed
    2 months ago
    Total: 198s
    #308065
  • First commit to issue fork.
  • Pipeline finished with Failed
    2 months ago
    Total: 139s
    #308744
  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia mstrelan

    Fixed phpcs and updated the baseline in this MR, but also opened ๐Ÿ“Œ Add return types to UserCreationTrait Active for a proper fix so we don't need to keep adding to the baseline.

  • Pipeline finished with Failed
    2 months ago
    Total: 668s
    #308754
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Thanks!!

    I'm pondering whether to move the methods added to KernelTestBase to a trait, where I'd also add a helper method for setting the active theme.

    Thoughts on doing this (and also on what to call the trait? I'm thinking KernelTestUiHelperTrait)

  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia mstrelan

    I'd be interested to see if we can use something like this in Drupal Test Traits, to perform a drupalGet without the browser. So for that it would be nice to have these in a trait, and not necessarily with the words "kernel test" in it. But having the theme stuff in that same trait would not be useful in that regard, but perhaps wouldn't hurt.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    > So for that it would be nice to have these in a trait, and not necessarily with the words "kernel test" in it.

    We need something in the name to differentiate it from UiHelperTrait which is for browser tests. HttpKernelUiHelperTrait?

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    It's been a week with no objections to either the plan or the name, so I've moved the code for making requests to a trait.

  • Pipeline finished with Canceled
    about 2 months ago
    Total: 69s
    #315741
  • Pipeline finished with Failed
    about 2 months ago
    Total: 126s
    #315742
  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia larowlan ๐Ÿ‡ฆ๐Ÿ‡บ๐Ÿ.au GMT+10

    This looking great, we can mark #2469717: Step 3: Create a KernelWebTestBase base class/driver โ†’ as a duplicate

    When we start doing conversions, we should work on adding base-classes and traits first. The amount of additional setup required for e.g. a node module test is going to be a fair bit, so having that in all the tests would mean a lot of duplication otherwise.

    Needs a reroll at present

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Rerolled.

  • Pipeline finished with Failed
    about 2 months ago
    Total: 143s
    #322907
  • Pipeline finished with Failed
    about 1 month ago
    Total: 151s
    #329961
  • First commit to issue fork.
  • Pipeline finished with Canceled
    about 1 month ago
    Total: 126s
    #330243
  • Pipeline finished with Failed
    about 1 month ago
    Total: 238s
    #330245
  • Pipeline finished with Failed
    about 1 month ago
    Total: 735s
    #330252
  • Pipeline finished with Success
    about 1 month ago
    Total: 1092s
    #330556
  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia mstrelan

    I did some further tinkering with this. Wondering if we actually need our own ::drupalGet in the trait rather than just using the one from UiHelperTrait? I tried it out and it worked, but I had to implement ::prepareRequest as an empty function.

    I wonder if drupalGet, drupalLogin and drupalLogout should be moved out of UiHelperTrait, they have nothing to do with the UI, whereas the rest of that trait has methods like click, clickLink, etc which make a lot more sense. Then we would rename this trait something like HttpKernelHelperTrait (without the word UI) and it would use whatever trait has drupalGet. Same applies for the assertSession method, we don't need to duplicate it if we can share the existing one.

    Not sure if this helps or just complicate things.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    My plan with this issue -- and I see it's not stated above, but I do remember discussing it with people, probably @longwave? -- was to add the functionality in this issue, and then in a follow-up do refactoring for shared code with browser tests.

    I think I'd rather keep the two drupalGet()s separate as there are things in one that aren't relevant at all in the other -- for example, every request calling isTestUsingGuzzleClient() makes no sense in a kernel test.

    However, if you think HttpKernelHelperTrait is a better name than HttpKernelUiHelperTrait, to match up with future refactoring, then let's change that. I'm not sure about 'Ui' in a trait about making requests either.

    I'm not sure about the latest commit --

    -  protected function drupalGet($path, array $options = [], array $headers = []): void {
    +  protected function drupalGet($path, array $options = [], array $headers = []): string {
    

    The browser test drupalGet() doesn't return anything, so why should the kernel test version?

  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia mstrelan

    Ok that all makes sense about refactoring and keeping them separate.

    The browser test drupalGet() doesn't return anything, so why should the kernel test version?

    It does return something:

       * @return string
       *   The retrieved HTML string, also available as $this->getRawContent()
    

    See: https://git.drupalcode.org/project/drupal/-/blob/11.0.5/core/tests/Drupa...

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Oh. I was looking at the actual return type... :/

    Ok in that case setting back to needs review.

  • Assigned to joachim
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Something for a follow-on: I think I've worked out how to get page caching working with this.

    I'm working on ๐Ÿ› Cacheability is not respected when not using a lazy builder Active and I can get the test to fail (as expected) if I do this:

    - enable page_cache module
    - kill CommandLineOrUnsafeMethod, which prevents the page cache from handling the request.

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Rebased.

  • Pipeline finished with Failed
    16 days ago
    Total: 162s
    #357943
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany geek-merlin Freiburg, Germany

    Looks like tests are red..

  • Pipeline finished with Failed
    7 days ago
    Total: 137s
    #367606
  • First commit to issue fork.
  • Pipeline finished with Success
    6 days ago
    #367920
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom joachim

    Tests are green and @alexpott has made the changes from his review, so this is back to NR.

Production build 0.71.5 2024