BDD

Behaviour Driven Development
http://goo.gl/e2MgBr
Jorge Tutor / @jltutor
#DrupalCampEs

Jorge Tutor

Software Project Manager & Drupal Senior Developer

drupal.org/u/gedur
Module maintainer: Ga Push, Commerce Google Analytics, IP, Misery...

In this session we will talk about:

  1. Introduction to BDD
  2. How projects are built
  3. Ensuring product quality
  4. Speaking our client's language
  5. The BDD methodology
  6. The integration with Drupal
  7. Final Example
Chapter 1

Introduction to BDD

What is BDD

Software development process that emerged from test-driven development (TDD).

An idea about how software development should be managed by both business interests and technical insight.

It combines the general techniques and principles of TDD with ideas from domain-driven design and object-oriented analysis and design to provide software development and management teams with shared tools and a shared process to collaborate on software development.

Clients and users have needs
and they want systems to satisfy them

The functionality of an application
is a set of all system features

The sum of features makes the project

Chapter 2

How projects are built

The idea

Corporate site, Newspaper, E-Commerce, Social network...

The Client

Stakeholders

The development team

Site Builders, Frontends, Backends, Testers...

Reliable Set of Components

Modules, Libraries, Themes...

Custom Components

Modules, Libraries, Themes...
Chapter 3

Ensuring product quality

Introduction to testing

Why to test?

Ensure components quality, check specifications, avoid regressions...

When should we test

After Development vs Before Development

Testing at the end of the project

Testing at the end of each sprint

Testing at the beginning of each sprint

How to test functionality

Unit testing

Components of a software are tested, ...each unit of the software performs as designed.
A unit is the smallest testable part of software.


class StackTest extends TestCase {
  public function testPushAndPop()
  {
      $stack = [];
      $this->assertEquals(0, count($stack));

      array_push($stack, 'foo');
      $this->assertEquals('foo', $stack[count($stack)-1]);
      $this->assertEquals(1, count($stack));

      $this->assertEquals('foo', array_pop($stack));
      $this->assertEquals(0, count($stack));
  }
}
  

Integration testing

Individual software modules are combined and tested as a group.
After unit testing.

Functional testing

Test the features/functionality of the system or Software, should cover all the scenarios including failure paths and boundary cases.


class Breadcrumb404Test extends BrowserTestBase {
  ...
  public function testBreadcrumbOn404Pages() {
    ...
    $this->drupalGet('/not-found-2');
    $next_count = count($this->getBreadcrumbCacheEntries());
    $this->assertEquals($base_count, $next_count);

    $this->drupalGet('/not-found-3');
    $next_count = count($this->getBreadcrumbCacheEntries());
    $this->assertEquals($base_count, $next_count);
  }
  ...
}
  

Wait!!!

I just use components tested by unit tests, It's enough!!!

If all the pieces runs ok, then the site will run ok.

2 unit tests. 0 integration tests.
2 unit tests. 0 integration tests.
2 unit tests. 0 integration tests.

...view more in reddit.com/r/ProgrammerHumor/search?q=unit

All unit, integration,... tests passed but,
Have we created the right product?

Have we also failed?

Verification vs Validation

Determine whether they meet the specified requirements. Determine whether it satisfies specified business requirements.
Are we building the
product right?
Are we building the
right product?
Chapter 4

Speaking our client's language

What client says

Project specifications/requirements

System perspective, just characteristics, no user interaction

What developers need

User workflow perspective, mockups, user interaction, edge use cases

What users need

User centered design

Let's use the same language

User Stories

User perspective, focused on user value

As a { who }
I want to { what }
so that { why }

User Stories: Examples

As an anonymous I want to register into the site so that I can access to the private section

As an editor I want to create articles so that the site will have new content

As an editor I want to edit articles so that I can update them and fix mistakes

As a manager I want to cancel user accounts so that I can keep the accounts under control

As a sales manager I want to download sales reports in csv so that I can work with them later

A user story is defined by a set of use cases

Use Cases

Describe concrete interactions for each User Story. Acceptance criteria.

As an anonymous I want to register into the site so that I can access to the private section

  • Given I am an anonymous user and I don't have an account
  • When I go to user register
  • I should see the fields: name, surname, email and password
  • I fill name with "TestName"
  • ...
  • Then I press Create account
  • Then I should see "Welcome TestName"
  • An account for "TestName" has been created

Use Case anatomy

  1. Preconditions
  2. Actions
  3. Alternative states
  4. Error states
  5. Postconditions

Use Case anatomy example

Preconditions Given I am an anonymous user
Actions When I go to user register
I should see the fields: name, surname, email and password
I fill name with "TestName"
...
I press "Create account"
Postconditions An account for "TestName" has been created

Use Case Alternative steps

Along with the case, alternative paths may show up

Use Case Alternative steps example

As an anonymous I want to register into the site so that I can access to the private section

...

4. I select "individual" as user type
5. I fill user name with "Peter"
...
A individual account for "Peter" has been created


4.A. I select "company" as user type
5.A. I fill company name with "Drupal"
...
A company account for "Drupal" has been created

Use case error states

Alternative steps can finalize in error steps

Sunny Day: use case without errors

Use case error states example

4. I select "individual" as user type
5. I fill user name with "Peter"
6. I fill mail with "FAKE@@MAIL..cc"
...
10. And I press "Create Account"

Optimizing Use case scenarios

Writing use cases can be boring, use strategies to optimize (without losing readability).

Chapter 5

The BDD Methodology

BDD Methodology

  1. Define a test set for the user story: use cases
  2. Make the test fail: there is nothing implemented yet
  3. Then implement the unit: development
  4. Finally verify that the implementation of the unit makes the tests succeed
I wish there was a way to automate test use cases

Gherkin language


	Story: As a store owner
	I want to add items back to stock when they're returned
	so that I can keep track of stock

	Scenario 1: Refunded items should be returned to stock
	Given that a customer previously bought a black sweater from me
	And I have three black sweaters in stock.
	When he returns the black sweater for a refund
	Then I should have four black sweaters in stock.

	Scenario 2: Replaced items should be returned to stock
	Given that a customer previously bought a blue garment from me
	And I have two blue garments in stock
	And three black garments in stock.
	When he returns the blue garment for a replacement in black
	Then I should have three blue garments in stock
	And two black garments in stock.

Gherkin language syntax example. The term Gherkin, however, is specific to the Cucumber, JBehave and Behat software tools.

A php framework for autotesting your business expectations.

Behat is an open source Behaviour-Driven Development framework for PHP. It is a tool to support you in delivering software that matters through continuous communication, deliberate discovery and test-automation.

behat.org

Chapter 6

The Integration with Drupal

Behat installation


# Create the directory:
$ mkdir /opt/behat && cd /opt/behat

# Install with composer:
$ composer require drupal/drupal-extension

# Symbolic link to $PATH
$ ln -s /opt/behat/vendor/bin/behat /usr/local/bin/behat

# Check version:
$ behat --version
					

installation for Drupal projects

Behat files & folders

config behat.yml
Contexts /bootstrap (folder)
Features /features (folder)

Detailed installation and configuration see this post

Behat files & folders structure


Drupal
└─ sites/
   ├─ all/
   │  └─ test/
   │     └─ behat/
   │        ├─ features/
   │        │  ├─ anonymous.access.feature ← User Stories
   │        │  └─ *.feature
   │        │
   │        └─ bootstrap/
   │           ├─ FeatureContext.php ← Base Context
   │           └─ ... ← Other Contexts
   └─ default/
       └─ behat.yml ← Conf yml

					

Behat execution


# Go to drupal docroot:
$ cd /var/vhost/mysproject/docroot

# Execute behat:
$ behat --config sites/default/behat.yml --tags="register"

					

Execution for Drupal project

Behat processing


								@check @anonymous @statistics
								Scenario: Check, Statistics and Statistics menu                                       # /vagrant/vhosts/project/docroot/sites/all/tests/behat/features/project/user_anonymous.feature:71
						    Given I am an anonymous user                                                        # FeatureContext::assertAnonymousUser()
						    And I go to "/"                                                                     # Drupal\DrupalExtension\Context\MinkContext::visit()
						    When I click "Statistics" in the "main menu"                                        # Drupal\DrupalExtension\Context\MinkContext::assertRegionLinkFollow()
						    Then the url should match "/statistics"                                             # Drupal\DrupalExtension\Context\MinkContext::assertUrlRegExp()
						    And I should see "Statistics" in the "statistics menu pane full"                    # Drupal\DrupalExtension\Context\MinkContext::assertRegionText()
						    And I should see the link "World Cup Ranking" in the "panel column last"            # Drupal\DrupalExtension\Context\MinkContext::assertLinkRegion()
						    And I should see the link "World Ranking" in the "panel column last"                # Drupal\DrupalExtension\Context\MinkContext::assertLinkRegion()
						    And I should see the link "World Records" in the "panel column last"                # Drupal\DrupalExtension\Context\MinkContext::assertLinkRegion()
						    And I should see the link "Longines Prize for Precision" in the "panel column last" # Drupal\DrupalExtension\Context\MinkContext::assertLinkRegion()
						    When I click "World Cup Ranking" in the "panel column last"                         # Drupal\DrupalExtension\Context\MinkContext::assertRegionLinkFollow(
								  ...
					

Example of execution steps

Behat results

Sucessful execution

						48 scenarios (48 passed)
						773 steps (773 passed)
					
Failed execution

					--- Failed scenarios:

					    ../features/authenticated.donation_create.feature:17
					    ../features/editor.news_edit.feature:9

					21 scenarios (19 passed, 2 failed)
					289 steps (274 passed, 2 failed, 13 skipped)
					

Integrate it with your CI tool!

jenkins screenshot jenkins screenshot
Chapter 7

Final example

User Stories

As an anonymous
I want to register into the site
so that I can join the program

As an anonymous
I want to pull data from Linkedin while I'm registering into the site
so that I can do it easier

The feature file

As an anonymous I want to register into the site so that I can join the program


file: anonymous.user_register.feature

							Feature: As an anonymous
								I want to register into the site
								so that I can join the program

							@TODO: Scenario SunnyDay
							@TODO: Scenario Alternative 1..N
							@TODO: Scenario Errors 1..N
							
							

Sunny day

As an anonymous I want to register into the site so that I can join the program


file: anonymous.user_register.feature

							@anonymous @user-register @sunnyday
							Scenario: Fills all fields
							  and joins the program as invididual

							  # Preconditions:
							  Given I am an anonymous user
							    And I go to "/user/register"

							  # Actions:
							  Then I should see "Type"
							   And I should see "Name"
							   And I should see "Surname"
							   And I should see "Why do you want to join the program?"
							   And I should see "I accept the terms & conditions"

							  Then I select "individual" from "Type"
							  Then I fill "Name" with "TestName"
							   And I fill "Surname" with "TestSurname"
							   And I fill "Why do you want to join the program?" with "Some text..."
							   And I press "I accept the terms & conditions"

							  When I click "Create Account"
							  Then I should see "Welcome to the program!"

							  # Posconditions:
							  Then an individual account has been created for "TestName"
								...
							

Alternative steps

As an anonymous I want to register into the site so that I can join the program


file: anonymous.user_register.feature

							@anonymous @user-register @variant
							Scenario: Fills all fields
							  and joins the program as company

							  # Preconditions:
							  Given I am an anonymous user
						      And I go to "/user/register"

							  # Actions:
							  Then I select "company" from "Type"
							   And I should not see "Surname"
							   And I fill "Name" with "TestName"
							   And I fill "Why do you want to join the program?" with "Some text..."
							   And I press "I accept the terms & conditions"

							  When I click "Create Account"
							  Then I should see "Welcome to the program!"

							  # Posconditions:
							  Then a company account has been created for "TestName"
								...
							

Error steps

As an anonymous I want to register into the site so that I can join the program


file: anonymous.user_register.feature

@anonymous @user-register @error
Scenario: Fails filling fields

  # Preconditions:
  Given I am an anonymous user
    And I go to "/user/register"

  # Actions:
  When I click "Create Account"

  Then I should see the message
	 "The field name is required"
   And I should see the message
	 "The field surname is required"
   And I should see the message
	 "The field Why do you want to join the program? is required"
   And I should see the message
	 "The field I accept the terms & conditions is required"

  Then I fill "Why do you want to join the program?"
	 with "Some short text..."

  When I click "Create Account"

  Then I should see the message "The field Why do you want to join the program?
	 should have 300 characters at least"
   ...
							
Let's try the other one

The feature file

As an anonymous I want to pull data from Linkedin while I'm registering into the site so that I can do it easier


file: anonymous.user_register.linkedin.feature

							Feature: As an anonymous
							  I want to pull data from Linkedin while I'm registering into the site
							  so that I can do it easier

							@TODO: Scenario SunnyDay
							@TODO: Scenario Alternative 1..N
							@TODO: Scenario Errors 1..N
							
							

Sunny day

As an anonymous I want to pull data from Linkedin while I'm registering into the site so that I can do it easier


file: anonymous.user_register.linkedin.feature

								@anonymous @user-register @Linkedin @sunnyday
								Scenario: Fills all fields with Linkedin data
								  and joins the program as invididual

								  # Preconditions:
								  Given I am an anonymous user
								    And I go to "/user/register"
								    And I have a Linkedin account

								  # Actions:
								  When I click "Pull data from Linkedin"
								   And I login in Linkedin and give permissions

								   Then I should see the field "Name"
								     filled with "LinkedinTestName"
								    And I should see the field "Surname"
								     filled with "LinkedinTestSurName"

								  Then I select "individual" from "Type"
								   And I fill "Why do you want to join the program?"
								    with "Some text..."
								   And I press "I accept the terms & conditions"

								  When I click "Create Account"
								  Then I should see "Welcome to the program!"

								  # Posconditions:
								  Then an individual account has been created
								    for "LinkedinTestName"
								  ...
							

Alternative Steps

As an anonymous I want to pull data from Linkedin while I'm registering into the site so that I can do it easier


Company? Incomplete profile?

Error Steps

As an anonymous I want to pull data from Linkedin while I'm registering into the site so that I can do it easier


No Linkedin loggin? No privileges granted?

Easy?

Willing to write your own BDD tests?

Any questions?


						Given I am an attendee
						  And I am brave enough to ask a question

						When I ask for the microphone
						 And I ask a question to the speaker

						Then I get an answer [ maybe a good one :) ]
					

Thanks!!!

@jltutor