Logging in as an existing user in a Behat test with the Drupal Extension

There are some occasions when I want my Drupal Behat tests to perform some action as a user that already exists on the Drupal site. For example, I have a test install profile with some Default Content (users, nodes, taxonomy terms, etc.), and it already has a large set of default test data set up on the site for the benefit of developers who need to work on theming/site building.

Rather than define a ton of extra Behat steps to re-create all this test content and these test users, I just want Behat to log in as an existing user and perform actions with the pre-existing content.

Note that this might not be a good idea depending on the structure or philosophy of your site's testing. As a general principle, state should be avoided—and that includes things like 'having a set of default stuff already existing before a test runs'. However, in the real world there are situations where it's a ton easier to just use the state that exists ?.

The problem

Out of the box, the Drupal API Driver lets you create a user in a Scenario and then use that created user, like so:

  Scenario: Login as a user created during this scenario
    Given users:
    | name      | status |
    | Test user |      1 |
    When I am logged in as "Test user"
    Then I should see the link "Log out"

But if you try to log in as an existing user, e.g.:

  Scenario: Login as an existing user
    Given I am logged in as "Existing user"
    Then I should see the link "Log out"

You'll get the error:

Exception: No user with Existing user name is registered with the driver. in /var/www/html/vendor/drupal/drupal-extension/src/Drupal/DrupalUserManager.php:57

The Solution

There are two ways you can solve this problem:

  1. You can write a bunch of steps to go to the login page, fill in a username and password, and click 'Log In', then do the rest of your scenario.
  2. You can write your own step definition, e.g. iAmLoggedInAsUser() .

The first option is okay, but then you have to store a password somewhere in your code (which just feels dirty, even if it's just for testing), and it's a few extra steps every time you want to run a scenario as that user !

For the second option, I quickly found the following issue in the Drupal Extension's issue queue: Use drush user-login to login user . Taking some of the examples from there, I came up with the following step definition in my FeatureContext.php file:

<?php
  /**
   * @Given I am logged in as user :name
   */
  public function iAmLoggedInAsUser($name) {
    $domain = $this->getMinkParameter('base_url');

    // Pass base url to drush command.
    $uli = $this->getDriver('drush')->drush('uli', [
      "--name '" . $name . "'",
      "--browser=0",
      "--uri=$domain",
    ]);

    // Trim EOL characters.
    $uli = trim($uli);

    // Log in.
    $this->getSession()->visit($uli);
  }
?>

Now I can write my Scenario like so:

  @api @javascript
  Scenario: Log in as Existing user
    Given I am logged in as user "Existing user"
    And I am on "/"
    Then I should see the link "Log out"