jhedstrom / drupalextension

An integration layer between Behat, Mink Extension, and Drupal.
GNU General Public License v2.0
209 stars 192 forks source link

Error when disabling subcontexts: "Drupal::$container is not initialized yet" #576

Open pfrenssen opened 3 years ago

pfrenssen commented 3 years ago

Subcontexts have been deprecated in #518 and are planned to be removed in #549

In the current development version of BDE subcontexts are still autoloaded, but this can be disabled as follows:

default:
  extensions:
    Drupal\DrupalExtension:
      subcontexts:
        autoload: false
        paths: []

However this removes the only way that Drupal is automatically bootstrapped at the start of a test suite. This is currently happening in Drupal\DrupalExtension\Context\Environment\Reader\Reader::findSubContextClasses() which calls $driver->getSubDriverPaths() which calls $this->bootstrap().

If the subcontext autoloading is disabled Drupal is no longer bootstrapped and this leads to this error:

\Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container. (Drupal\Core\DependencyInjection\ContainerNotInitializedException)

pfrenssen commented 3 years ago

A simple workaround for this is to implement an @BeforeScenario hook:

  /**
   * Bootstraps Drupal.
   *
   * We need to ensure that Drupal is properly bootstrapped before we run any
   * other hooks or execute step definitions. By calling `::getDriver()` we can
   * be sure that Drupal is ready to rock.
   *
   * This hook should be placed at the top of the first Context class listed in
   * behat.yml.
   *
   * @BeforeScenario @api
   */
  public function bootstrap(): void {
    $driver = $this->getDriver();
    if (!$driver->isBootstrapped()) {
      $driver->bootstrap();
    }
  }
brooke-heaton commented 2 years ago

Hm, strange. I've added the above solution to my FeatureContext.php and am still getting the Error. FeatureContext is the first Context listed in my behat.yml

jhedstrom commented 2 years ago

Can you run with the -v option and post a stack trace? That might be helpful to pinpoint what is asking for the container so early on.

brooke-heaton commented 2 years ago

@jhedstrom

brookeheaton@pop-os:~/Sites/naswa$ lando behat --tags="@member-role" -vvv
lando 13:55:48 DEBUG ==> No update available. 
lando 13:55:48 VERBOSE ==> starting bootstrap at level engine... 
lando 13:55:48 VERBOSE ==> config bootstrap beginning... 
lando 13:55:48 DEBUG ==> emitting event pre-bootstrap-config 
lando 13:55:48 DEBUG ==> plugin lando-core loaded from /snapshot/cli/plugins/lando-core/index.js 
lando 13:55:48 DEBUG ==> plugin lando-events loaded from /snapshot/cli/plugins/lando-events/index.js 
lando 13:55:48 DEBUG ==> plugin lando-networking loaded from /snapshot/cli/plugins/lando-networking/index.js 
lando 13:55:48 DEBUG ==> plugin lando-proxy loaded from /snapshot/cli/plugins/lando-proxy/index.js 
lando 13:55:48 DEBUG ==> plugin lando-recipes loaded from /snapshot/cli/plugins/lando-recipes/index.js 
lando 13:55:48 DEBUG ==> plugin lando-services loaded from /snapshot/cli/plugins/lando-services/index.js 
lando 13:55:48 DEBUG ==> plugin lando-sharing loaded from /snapshot/cli/plugins/lando-sharing/index.js 
lando 13:55:48 DEBUG ==> plugin lando-test loaded from /snapshot/cli/plugins/lando-test/index.js 
lando 13:55:48 DEBUG ==> plugin lando-tooling loaded from /snapshot/cli/plugins/lando-tooling/index.js 
lando 13:55:48 DEBUG ==> plugin lando-acquia loaded from /snapshot/cli/integrations/lando-acquia/index.js 
lando 13:55:48 DEBUG ==> plugin lando-lagoon loaded from /snapshot/cli/integrations/lando-lagoon/index.js 
lando 13:55:48 DEBUG ==> plugin lando-pantheon loaded from /snapshot/cli/integrations/lando-pantheon/index.js 
lando 13:55:48 DEBUG ==> plugin @lando/platformsh loaded from /snapshot/cli/node_modules/@lando/platformsh/index.js 
lando 13:55:48 DEBUG ==> emitting event post-bootstrap-config 
lando 13:55:48 VERBOSE ==> building proxy config... 
lando 13:55:48 VERBOSE ==> config bootstrap completed. 
lando 13:55:48 VERBOSE ==> tasks bootstrap beginning... 
lando 13:55:48 DEBUG ==> emitting event pre-bootstrap-tasks 
lando 13:55:48 DEBUG ==> autoloaded task config 
lando 13:55:48 DEBUG ==> autoloaded task destroy 
lando 13:55:48 DEBUG ==> autoloaded task info 
lando 13:55:48 DEBUG ==> autoloaded task list 
lando 13:55:48 DEBUG ==> autoloaded task logs 
lando 13:55:48 DEBUG ==> autoloaded task poweroff 
lando 13:55:48 DEBUG ==> autoloaded task rebuild 
lando 13:55:48 DEBUG ==> autoloaded task restart 
lando 13:55:48 DEBUG ==> autoloaded task share 
lando 13:55:48 DEBUG ==> autoloaded task start 
lando 13:55:48 DEBUG ==> autoloaded task stop 
lando 13:55:48 DEBUG ==> autoloaded task version 
lando 13:55:48 DEBUG ==> autoloaded task init 
lando 13:55:48 DEBUG ==> autoloaded task ssh 
lando 13:55:48 DEBUG ==> emitting event post-bootstrap-tasks 
lando 13:55:48 VERBOSE ==> tasks bootstrap completed. 
lando 13:55:48 VERBOSE ==> engine bootstrap beginning... 
lando 13:55:48 DEBUG ==> emitting event pre-bootstrap-engine 
lando 13:55:48 DEBUG ==> automoved scripts from /snapshot/cli/plugins/lando-core/scripts to /home/brookeheaton/.lando/scripts and set to mode 755 
lando 13:55:48 DEBUG ==> automoved scripts from /snapshot/cli/plugins/lando-proxy/scripts to /home/brookeheaton/.lando/scripts and set to mode 755 
lando 13:55:48 DEBUG ==> automoved scripts from /snapshot/cli/plugins/lando-recipes/scripts to /home/brookeheaton/.lando/scripts and set to mode 755 
lando 13:55:48 DEBUG ==> automoved scripts from /snapshot/cli/plugins/lando-services/scripts to /home/brookeheaton/.lando/scripts and set to mode 755 
lando 13:55:48 DEBUG ==> automoved scripts from /snapshot/cli/plugins/lando-sharing/scripts to /home/brookeheaton/.lando/scripts and set to mode 755 
lando 13:55:48 DEBUG ==> automoved scripts from /snapshot/cli/integrations/lando-acquia/scripts to /home/brookeheaton/.lando/scripts and set to mode 755 
lando 13:55:48 DEBUG ==> automoved scripts from /snapshot/cli/integrations/lando-lagoon/scripts to /home/brookeheaton/.lando/scripts and set to mode 755 
lando 13:55:48 DEBUG ==> automoved scripts from /snapshot/cli/integrations/lando-pantheon/scripts to /home/brookeheaton/.lando/scripts and set to mode 755 
lando 13:55:48 DEBUG ==> automoved scripts from /snapshot/cli/node_modules/@lando/platformsh/scripts to /home/brookeheaton/.lando/scripts and set to mode 755 
lando 13:55:48 DEBUG ==> emitting event post-bootstrap-engine 
lando 13:55:48 VERBOSE ==> engine bootstrap completed. 
lando 13:55:48 VERBOSE ==> bootstrap completed. 
lando 13:55:48 DEBUG ==> emitting event post-bootstrap 
lando 13:55:48 VERBOSE ==> docker-engine exists: true 
lando 13:55:48 VERBOSE ==> docker-compose exists: true 
lando 13:55:48 DEBUG ==> emitting event cli-answers 
lando 13:55:48 DEBUG ==> emitting event cli-behat-answers 
lando 13:55:48 DEBUG ==> emitting event cli-run 
lando 13:55:48 DEBUG ==> emitting event cli-behat-run 
lando 13:55:48 DEBUG ==> emitting event pre-command-runner 
lando 13:55:48 DEBUG ==> emitting event pre-behat 
lando 13:55:48 DEBUG ==> process pid4 running /usr/bin/docker exec naswa_appserver_1 /app/vendor/bin/behat /app/tests/behat/behat.yml cstdio=[inherit, pipe, pipe], silent=false, mode=attach, detached=false
lando 13:55:48 DEBUG ==> engine is up. 
                                       lando 13:55:48 DEBUG ==> docker is running. 
                                                                                   @api @member-role @javascript
Feature: Member Role
  In order to test the permissions and capabilities of the 'Member' role
  As a Member
  I should be able to do the things that Members can do

lando 13:55:49 VERBOSE ==> checking docker version compatibility... 
                                                                    lando 13:55:49 DEBUG ==> compatibility results name=compose, link=https://docs.docker.com/compose/install/#install-compose-on-linux-systems, wants=1.23.0 - 1.30.0, version=1.29.2, semversion=1.29.2, semmin=1.23.0, semmax=1.30.0, dockerVersion=true, satisfied=true, name=engine, link=https://docs.docker.com/engine/install/, wants=18.09.3 - 20.10.99, version=19.03.13, semversion=19.3.13, semmin=18.9.3, semmax=20.10.99, dockerVersion=true, satisfied=true
                                                                                                                ┌─ @BeforeScenario # FeatureContext::cleanupTestUsers()
  │
  ╳  Drupal\Core\DependencyInjection\ContainerNotInitializedException: \Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container. in /app/web/core/lib/Drupal.php:170
  ╳  Stack trace:
  ╳  #0 /app/web/core/lib/Drupal.php(314): Drupal::getContainer()
  ╳  #1 /app/web/core/lib/Drupal.php(506): Drupal::entityTypeManager()
  ╳  #2 features/bootstrap/FeatureContext.php(529): Drupal::entityQuery('user')
  ╳  #3 /app/vendor/behat/behat/src/Behat/Testwork/Call/Handler/RuntimeCallHandler.php(110): FeatureContext->cleanupTestUsers(Object(Behat\Behat\Hook\Scope\BeforeScenarioScope))
  ╳  #4 /app/vendor/behat/behat/src/Behat/Testwork/Call/Handler/RuntimeCallHandler.php(64): Behat\Testwork\Call\Handler\RuntimeCallHandler->executeCall(Object(Behat\Testwork\Hook\Call\HookCall))
  ╳  #5 /app/vendor/behat/behat/src/Behat/Testwork/Call/CallCenter.php(138): Behat\Testwork\Call\Handler\RuntimeCallHandler->handleCall(Object(Behat\Testwork\Hook\Call\HookCall))
  ╳  #6 /app/vendor/behat/behat/src/Behat/Testwork/Call/CallCenter.php(96): Behat\Testwork\Call\CallCenter->handleCall(Object(Behat\Testwork\Hook\Call\HookCall))
  ╳  #7 /app/vendor/behat/behat/src/Behat/Testwork/Hook/HookDispatcher.php(74): Behat\Testwork\Call\CallCenter->makeCall(Object(Behat\Testwork\Hook\Call\HookCall))
  ╳  #8 /app/vendor/behat/behat/src/Behat/Testwork/Hook/HookDispatcher.php(58): Behat\Testwork\Hook\HookDispatcher->dispatchHook(Object(Behat\Behat\Hook\Scope\BeforeScenarioScope), Object(Behat\Behat\Hook\Call\BeforeScenario))
  ╳  #9 /app/vendor/behat/behat/src/Behat/Behat/Hook/Tester/HookableScenarioTester.php(64): Behat\Testwork\Hook\HookDispatcher->dispatchScopeHooks(Object(Behat\Behat\Hook\Scope\BeforeScenarioScope))
  ╳  #10 /app/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingScenarioTester.php(92): Behat\Behat\Hook\Tester\HookableScenarioTester->setUp(Object(Behat\Behat\Context\Environment\InitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\ScenarioNode), false)
  ╳  #11 /app/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/IsolatingScenarioTester.php(67): Behat\Behat\EventDispatcher\Tester\EventDispatchingScenarioTester->setUp(Object(Behat\Behat\Context\Environment\InitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\ScenarioNode), false)
  ╳  #12 /app/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeFeatureTester.php(84): Behat\Behat\Tester\Runtime\IsolatingScenarioTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\ScenarioNode), false)
  ╳  #13 /app/vendor/behat/behat/src/Behat/Behat/Hook/Tester/HookableFeatureTester.php(72): Behat\Behat\Tester\Runtime\RuntimeFeatureTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), false)
  ╳  #14 /app/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingFeatureTester.php(74): Behat\Behat\Hook\Tester\HookableFeatureTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), false)
  ╳  #15 /app/vendor/behat/behat/src/Behat/Testwork/Tester/Runtime/RuntimeSuiteTester.php(63): Behat\Behat\EventDispatcher\Tester\EventDispatchingFeatureTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), false)
  ╳  #16 /app/vendor/behat/behat/src/Behat/Testwork/Hook/Tester/HookableSuiteTester.php(73): Behat\Testwork\Tester\Runtime\RuntimeSuiteTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Testwork\Specification\GroupedSpecificationIterator), false)
  ╳  #17 /app/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingSuiteTester.php(75): Behat\Testwork\Hook\Tester\HookableSuiteTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Testwork\Specification\GroupedSpecificationIterator), false)
  ╳  #18 /app/vendor/behat/behat/src/Behat/Testwork/Tester/Runtime/RuntimeExercise.php(71): Behat\Testwork\EventDispatcher\Tester\EventDispatchingSuiteTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Testwork\Specification\GroupedSpecificationIterator), false)
  ╳  #19 /app/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingExercise.php(73): Behat\Testwork\Tester\Runtime\RuntimeExercise->test(Array, false)
  ╳  #20 /app/vendor/behat/behat/src/Behat/Testwork/Ordering/OrderedExercise.php(80): Behat\Testwork\EventDispatcher\Tester\EventDispatchingExercise->test(Array, false)
  ╳  #21 /app/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/ExerciseController.php(149): Behat\Testwork\Ordering\OrderedExercise->test(Array, false)
  ╳  #22 /app/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/ExerciseController.php(108): Behat\Testwork\Tester\Cli\ExerciseController->testSpecifications(Object(Symfony\Component\Console\Input\ArgvInput), Array)
  ╳  #23 /app/vendor/behat/behat/src/Behat/Testwork/Cli/Command.php(63): Behat\Testwork\Tester\Cli\ExerciseController->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ╳  #24 /app/vendor/symfony/console/Command/Command.php(255): Behat\Testwork\Cli\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ╳  #25 /app/vendor/symfony/console/Application.php(1009): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ╳  #26 /app/vendor/symfony/console/Application.php(273): Symfony\Component\Console\Application->doRunCommand(Object(Behat\Testwork\Cli\Command), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ╳  #27 /app/vendor/behat/behat/src/Behat/Testwork/Cli/Application.php(124): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ╳  #28 /app/vendor/symfony/console/Application.php(149): Behat\Testwork\Cli\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  ╳  #29 /app/vendor/behat/behat/bin/behat(34): Symfony\Component\Console\Application->run()
  ╳  #30 {main}
  │
jhedstrom commented 2 years ago

Line 2 up there appears to be the culprit:

features/bootstrap/FeatureContext.php(529): Drupal::entityQuery('user')
brooke-heaton commented 2 years ago

@jhedstrom Is it not possible to use entityQuery in FeatureContext? I'm confused. I've seen countless examples of using Drupal functions in FeatureContext. What am I doing wrong?

brooke-heaton commented 2 years ago

FWIW this was where I went hunting initially for inspiration and I simply approached this using D8 methods: https://www.metaltoad.com/blog/what-i-learned-today-drupal-behat-scenario-cleanup

Another example: https://github.com/CityOfBoston/boston.gov-d8/blob/c89fbdb62efc69a7739151ecffcf0645dc7c72ac/tests/behat/src/Contexts/FeatureContext.php#L863

jhedstrom commented 2 years ago

It should definitely be possible, but can depend on how your contexts are setup (for instance, is the api driver setup as Drupal in your behat.yml, etc)

brooke-heaton commented 2 years ago

@jhedstrom I'm using the Pantheon ootb Build Tools behat.yml which has drush. Is that wrong?

brooke-heaton commented 2 years ago

@jhedstrom Example: https://github.com/tambaslamin/covid-19-chs/blob/0d691ead8e9383dc207c9b6ba4a3d62f0d943ddb/tests/behat/behat-pantheon.yml

jhedstrom commented 2 years ago

Ah, indeed. When using the drush driver, your code won't be able to call native PHP code, as the Behat process is actually running on a different server than the one that is running Drupal itself.

If you look in some of the contexts that ship with the Drupal Behat Extension, you'll see code that calls a driver before executing commands. For instance in DrupalContext this code adds a role to a given user:

        // Assign the temporary role with given permissions.
        $this->getDriver()->userAddRole($user, $role);

It's been a while since I've worked with the drush driver, but I think it allows calling raw drush commands, so if you need to do an entity query, you could potentially do that. However, it may be easier to use the driver methods that are already supported in the drush driver itself (see the DrushDriver class.)