Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Behat hangs when there are multiple scenarios, but works on a single one

Tags:

I have Behat test cases written like so:

Feature: Checkout
In order to buy products
As a customer
I need to be able to checkout items in the cart

Background: 
    Given step 1
    And step 2

@Ready
Scenario: Deliver now
    When step 3
    Then step 4

@NoneReady
Scenario: Deliver later
    When step a
    Then step b
    And step c


@AddressNotCovered
Scenario: Address Not Covered
    When step i
    Then step ii

If I run behat on a single tag, it works just fine:

$ behat --tags=Ready
Feature: Checkout
  In order to buy products
  As a customer
  I need to be able to checkout items in the cart

  @Ready
  Scenario: Deliver now                                                                 # tests/features/Checkout/CheckOut.feature:9
    step 1
    And step 2 
    .. 

1 scenario (1 passed)
7 steps (7 passed)
0m3.85s (36.62Mb)

But if I run it on multiple tags, it hangs at the end of the first tag:

behat --tags=Ready,AddressNotCovered
Feature: Checkout
  In order to buy products
  As a customer
  I need to be able to checkout items in the cart

  @Ready
  Scenario: Deliver now                                                                 # tests/features/Checkout/CheckOut.feature:9
    Given step ..
    ..
    And .. 

    // hangs here

What am I doing wrong?

Environment

Laravel 5.4
Behat 3.1.0
PHP 7.1.23
PHPUnit 5.7.27

from my composer.json

"require": {
    "php": ">=5.5.9",
    "laravel/framework": "5.4.*",
    ..
    "behat/behat": "3.1.0",
    "laracasts/behat-laravel-extension": "^1.1",
},
"require-dev": {
    "phpunit/phpunit": "~5.7",
    "phpspec/phpspec": "~2.1",
    "johnkary/phpunit-speedtrap": "^1.0",
},

Behat.yml

default:
  extensions:
      Laracasts\Behat:
          env_path: .env.testing
  autoload:
    - ./tests/features/bootstrap
  suites:

    Checkout:
      paths: [./tests/features/Checkout]
      contexts: [CheckoutFeatureContext]

Update

I tried to create sample gherkin to illustrate the problem above. I ran into the same problem when trying to auto append snippets. Appending snippets worked with a single scenario, but failed on multiple scenarios:

working example: single scenario

# tests/features/Example/Example.feature

Feature: Example
In order to show dev team how to use behat/gherkin using background 
As a developer
I need to be able write gherkin using a background and multiple scenarios 
And all scenarios should run 

Background: 
    Givens setup condition 1 
    And setup condition 2 


Scenario: scenario one 
    When I perform first sample trigger point 
    Then result one must happen 
    And result two must happen 

When I run the following command

behat tests/features/Example/Example.feature  --append-snippets

adding snippets worked just fine

Feature: Example
  In order to show dev team how to use behat/gherkin using background
  As a developer
  I need to be able write gherkin using a background and multiple scenarios
  And all scenarios should run

  Background:             # tests/features/Example/Example.feature:9
      Givens setup condition 1
    And setup condition 2

  Scenario: scenario one                      # tests/features/Example/Example.feature:13
    When I perform first sample trigger point
    Then result one must happen
    And result two must happen

1 scenario (1 undefined)
4 steps (4 undefined)
0m0.48s (24.63Mb)

u tests/features/bootstrap/FeatureContext.php - `setup condition 2` definition added
u tests/features/bootstrap/FeatureContext.php - `I perform first sample trigger point` definition added
u tests/features/bootstrap/FeatureContext.php - `result one must happen` definition added
u tests/features/bootstrap/FeatureContext.php - `result two must happen` definition added

failing example: multiple scenarios

when we have multiple scenarios

# tests/features/Example/Example.feature

Feature: Example
In order to show dev team how to use behat/gherkin using background 
As a developer
I need to be able write gherkin using a background and multiple scenarios 
And all scenarios should run 

Background: 
    Givens setup condition 1 
    And setup condition 2 

Scenario: scenario one 
    When I perform first sample trigger point 
    Then result one must happen 
    And result two must happen 

Scenario: scenario two 
    When I perform second sample trigger point 
    Then result a must happen 
    And result b must happen 

running the same --append-snippets command chokes:

Feature: Example
  In order to show dev team how to use behat/gherkin using background
  As a developer
  I need to be able write gherkin using a background and multiple scenarios
  And all scenarios should run

  Background:             # tests/features/Example/Example.feature:9
      Givens setup condition 1
    And setup condition 2

  Scenario: scenario one                      # tests/features/Example/Example.feature:13
    When I perform first sample trigger point
    Then result one must happen
    And result two must happen

^C // had to abort here
like image 840
abbood Avatar asked Aug 08 '19 09:08

abbood


People also ask

How does Behat work?

Behat is a tool that makes behavior driven development (BDD) possible. With BDD, you write human-readable stories that describe the behavior of your application. These stories can then be auto-tested against your application. And yes, it's as cool as it sounds!

What is Behat test?

Behat is a PHP testing framework which can be used to automate acceptance tests in a human readable language called Gherkin. Since it is based on Cucumber, it also helps teams to adopt and implement Behavioral Driven Development (BDD).


1 Answers

It turns out the examples above were too simplistic. After doing some research (especially helpful was this post) I realized that this "stalling" is due to tearing down the database after each test. So this is what fixed it:

First I replaced DatabaseTransactions with DatabaseMigrations in my FeatureContext class:

class FeatureContext extends TestCase implements  Context, SnippetAcceptingContext
{

    use DatabaseMigrations, ..

Given the above, I removed the manual migration comand from my bitbucket pipeline script

- php artisan --env=testing config:cache

which makes sense since with the new code, the database will always be refreshed and migrated before each test.

Then I added the setUp() call to the behat hooks:

/** @BeforeScenario */
public function before(BeforeScenarioScope $scope)
{
    parent::setUp();
}

And that's it. The best part about this solution is that it completely aligned my local testing environment with that of bitbucket pipelines, so that the results were always the same.

Further explanation: from our wiki

In general, it's a good idea to start each test fresh without left overs from the previous test (esp when it comes to databases). In the words of laravel:

It is often useful to reset your database after each test so that data from a previous test does not interfere with subsequent tests.

For that we use migrations. That being said, since we're actually using Behat, we need this migration to happen before and after each scenario life cycle. We do that using Behat's hooks. We do that here:

/** @BeforeScenario */
    public function before(BeforeScenarioScope $scope)
    {
        parent::setUp();
    }

parent::setUP() tells the Laravel framework to do the necessary work before and after each scenario:

 protected function setUp()
    {
        if (! $this->app) {
            $this->refreshApplication();
        }
        $this->setUpTraits(); <---- here
        ..

This in turn calls setup traits:

   protected function setUpTraits()
    {
        $uses = array_flip(class_uses_recursive(static::class));
        if (isset($uses[DatabaseMigrations::class])) {
            $this->runDatabaseMigrations();
        }
        ..

which calls this

public function runDatabaseMigrations()
{
    $this->artisan('migrate:fresh');
    $this->app[Kernel::class]->setArtisan(null);
    $this->beforeApplicationDestroyed(function () {
        $this->artisan('migrate:rollback');
        RefreshDatabaseState::$migrated = false;
    });
}

Notice that Laravel will also rollback the changes once the application is destroyed. It's very important to understand this in order to prevent Behat stalling when there are multiple scenarios and a given before them. Also keep in mind that when we use Gherkin like so:

Feature: Checkout
In order to buy products
As a customer
I need to be able to checkout items in the cart

Background: 
    Given step 1
    And step 2

@Ready
Scenario: Deliver now
    When step 3
    Then step 4

@NoneReady
Scenario: Deliver later
    When step a
    Then step b
    And step c

Then each scenario starts with the background steps, not in the scenario steps itself

Example:

Feature: Checkout
In order to buy products
As a customer
I need to be able to checkout items in the cart

Background:
    Given step 1  <-- every scenario starts here, so we call setup before this step
    And step 2

@Ready
Scenario: Deliver now
    When step 3 <-- not here
    Then step 4

@NoneReady
Scenario: Deliver later
    When step a
    Then step b
    And step c
like image 67
abbood Avatar answered Sep 27 '22 19:09

abbood