SlideShare une entreprise Scribd logo
1  sur  38
Télécharger pour lire hors ligne
Behavior Driven Development (BDD)

Iwan van Staveren (@istaveren)
This presentation is of the author and does not necessarily reflect the view of Oracle
In order to verify application behavior
As a software developer
I need tests
In order to verify application behavior
As a software developer
I need tests
Preferably automated tests
Test-Driven Development
...is an iterative design process
●

Write a test
Test-Driven Development
...is an iterative design process
●

Write a test

●

Ensure the new test fails
Test-Driven Development
...is an iterative design process
●

Write a test

●

Ensure the new test fails

●

Write code to satisfy the test
Test-Driven Development
...is an iterative design process
●

Write a test

●

Ensure the new test fails

●

Write code to satisfy the test

●

Ensure all tests pass
Test-Driven Development
...is an iterative design process
●

Write a test

●

Ensure the new test fails

●

Write code to satisfy the test

●

Ensure all tests pass

●

Refactor
Dan North Introduces BDD

“

I had a problem. While using and teaching agile
practices like test-driven development (TDD) on
projects in different environments, I kept coming
across the same confusion and misunderstandings.
Programmers wanted to know:
●

Where to start

●

What to test and what not to test

●

How much to test in one go

●

What to call their tests

●

How to understand why a test fails
http://dannorth.net/introducing-bdd/
Dan North Introduces BDD

“

I started using the word “behavior” in place of “test” in
my dealings with TDD and… I now had answers to
some of those TDD questions:
●

●

●

What to call your test is easy – it’s a sentence
describing the next behavior in which you are
interested.
How much to test becomes moot – you can only
describe so much behavior in a single sentence.
When a test fails, simply work through the process
described above – either you introduced a bug, the
behavior moved, or the test is no longer relevant.
http://dannorth.net/introducing-bdd/
Behavior-Driven Development
...builds upon TDD
●

Write test cases in a natural language
●

●

●

Understood by developers and business folks alike
Helps relate domain language of requirements to the code

Do this with user stories and scenarios
●

User stories describe a feature's benefit in context

●

Scenarios are executable acceptance criteria

“

A story’s behavior is simply its acceptance
criteria – if the system fulfills all the
acceptance criteria, it’s behaving correctly;
if it doesn’t, it isn’t.

http://dannorth.net/introducing-bdd/
So what does this look like?
Example: A Contact Form
contact.feature
Feature: Contact form
In order to contact an email address
As a visitor
I need to be able to submit a contact form

Scenario: Successfully submit the contact form
Given I am on "/demo/contact"
When I fill in "Email" with "user@example.com"
And I fill in "Message" with "Hello there!"
And I press "Send"
Then I should see "Message sent!"
Example: A Contact Form
contact.feature
Feature: Contact form
Benefit

In order to contact an email address

Role

As a visitor

Feature

I need to be able to submit a contact form

Scenario: Successfully submit the contact form
Context

Given I am on "/demo/contact"
When I fill in "Email" with "user@example.com"

Events

And I fill in "Message" with "Hello there!"
And I press "Send"

Outcome

Then I should see "Message sent!"
This is where Behat and Mink come in.
Acceptance testing (any tests)
Tests a feature by executing its scenarios'
steps in a context.

This is where Behat and Mink come in.
Web acceptance testing (functional tests)
Drivers for Goutte, Sahi and Symfony2's
test client.
Add Behat to Symony
composer.json add
"require": {
...
"behat/symfony2-extension": "*",
"behat/mink-extension": "*",
"behat/mink-browserkit-driver": "*"
}
Run composer update
Create behat.yml
default:
# ...
extensions:
BehatSymfony2ExtensionExtension:
mink_driver: true
BehatMinkExtensionExtension:
default_session: 'symfony2'
Initialize a Bundle with Behat
$ bin/behat --init @AcmeDemoBundle
+d src/Acme/DemoBundle/Features
- place your *.feature files here
+f src/Acme/DemoBundle/Features/Context/FeatureContext.php
- place your feature related code here

●

●

We now have a directory to hold AcmeDemoBundle's features
Behat also creates an empty FeatureContext class, which
extends BehatBundle's BehatContext
●

Features describe our behavior, but the context tells Behat
how to evaluate our feature as an executable test
Let's Have Behat Analyze Our Feature
$ app/console behat src/Acme/DemoBundle/Features/contact.feature
Feature: Contact form
In order to contact an email address
As a visitor
I need to be able to submit a contact form

Scenario: Successfully submit the contact form
Given I am on "/demo/contact"
When I fill in "Email" with "user@example.com"
And I fill in "Message" with "Hello there!"
And I press "Send"
Then I should see "Message sent!"

1 scenario (1 undefined)
5 steps (5 undefined)

# contact.feature:6
Behat Creates the Glue
...but the rest is up to you
You can implement step definitions for undefined steps with these snippets:

/**
* @Given /^I am on "([^"]*)"$/
*/
public function iAmOn($arg1)
{
throw new PendingException();
}
/**
* @When /^I fill in "([^"]*)" with "([^"]*)"$/
*/
public function iFillInWith($arg1, $arg2)
{
throw new PendingException();
}

/**
* @Given /^I press "([^"]*)"$/
*/
public function iPress($arg1)
{
throw new PendingException();
}
/**
* @Then /^I should see "([^"]*)"$/
*/
public function iShouldSee($arg1)
{
throw new PendingException();
}
Not so fast. What about Mink?
MinkContext Defines Steps
...for making requests
Pattern

Description

Given /^I am on "(?P<page>[^"]+)"$/

Opens specified page

When /^I go to "(?P<page>[^"]+)"$/

Opens specified page

When /^I reload the page$/

Reloads current page

When /^I move backward one page$/

Moves backward one page in history

When /^I move forward one page$/

Moves forward one page in history

When /^I press "(?P<button>(?:[^"]|")*)"$/

Presses button with specified id|name|title|alt|value

When /^I follow "(?P<link>(?:[^"]|")*)"$/

Clicks link with specified id|title|alt|text
MinkContext Defines Steps
...for interacting with forms
Pattern

Description

When /^I fill in "(?P<field>(?:[^"]|")*)" with "(?
P<value>(?:[^"]|")*)"$/

Fills in form field with specified id|name|label|value

When /^I fill in "(?P<value>(?:[^"]|")*)" for "(?
P<field>(?:[^"]|")*)"$/

Fills in form field with specified id|name|label|value

When /^I fill in the following:$/

Fills in form fields with provided table

When /^I select "(?P<option>(?:[^"]|")*)" from "(?
P<select>(?:[^"]|")*)"$/

Selects option in select field with specified id|name|
label|value

When /^I check "(?P<option>(?:[^"]|")*)"$/

Checks checkbox with specified id|name|label|value

When /^I uncheck "(?P<option>(?:[^"]|")*)"$/

Unchecks checkbox with specified id|name|label|
value

When /^I attach the file "(?P<path>[^"]*)" to "(?
P<field>(?:[^"]|")*)"$/

Attaches file to field with specified id|name|label|
value
MinkContext Defines Steps
...for interacting with forms
Pattern

Description

When /^I fill in "(?P<field>(?:[^"]|")*)" with "(?
P<value>(?:[^"]|")*)"$/

Fills in form field with specified id|name|label|value

When /^I fill in "(?P<value>(?:[^"]|")*)" for "(?
P<field>(?:[^"]|")*)"$/

Fills in form field with specified id|name|label|value

When /^I fill in the following:$/

Fills in form fields with provided table

When /^I select "(?P<option>(?:[^"]|")*)" from "(?
P<select>(?:[^"]|")*)"$/

Selects option in select field with specified id|name|
label|value

When /^I check "(?P<option>(?:[^"]|")*)"$/

Checks checkbox with specified id|name|label|value

When /^I uncheck "(?P<option>(?:[^"]|")*)"$/

Unchecks checkbox with specified id|name|label|
value

When /^I attach the file "(?P<path>[^"]*)" to "(?
P<field>(?:[^"]|")*)"$/

Attaches file to field with specified id|name|label|
value

What's missing here?
Gherkin, the DSL Behat uses to define behaviors, specifies two
multi-line argument types: tables and pystrings
http://docs.behat.org/guides/1.gherkin.html#multiline-arguments

When I fill in the following:
| email
| user@example.com |
| message | Hello There!
|
Given lorem ipsum:
"""
This can be a multi-line string
"""
MinkContext Defines Steps
...for querying the DOM
Pattern

Description

Then /^I should see "(?P<text>(?:[^"]|")*)" in the "(?
P<element>[^"]*)" element$/

Checks that element with specified CSS contains
specified text

Then /^the "(?P<element>[^"]*)" element should
contain "(?P<value>(?:[^"]|")*)"$/

Checks that element with specified CSS contains
specified HTML

Then /^I should see an? "(?P<element>[^"]*)"
element$/

Checks that element with specified CSS exists on page

Then /^I should not see an? "(?P<element>[^"]*)"
element$/

Checks that element with specified CSS doesn't exist
on page

Then /^the "(?P<field>(?:[^"]|")*)" field should
contain "(?P<value>(?:[^"]|")*)"$/

Checks that form field with specified id|name|label|
value has specified value

Then /^the "(?P<field>(?:[^"]|")*)" field should not
contain "(?P<value>(?:[^"]|")*)"$/

Checks that form field with specified id|name|label|
value doesn't have specified value

Then /^the "(?P<checkbox>(?:[^"]|")*)" checkbox
should be checked$/

Checks that checkbox with specified id|name|label|
value is checked

Then /^the "(?P<checkbox>(?:[^"]|")*)" checkbox
should not be checked$/

Checks that checkbox with specified id|name|label|
value is unchecked
MinkContext Defines Steps
...for examining responses
Pattern

Description

Then /^I should be on "(?P<page>[^"]+)"$/

Checks that current page path is equal to specified

Then /^the url should match "(?P<pattern>(?:
[^"]|")*)"$/

Checks that current page path matches pattern

Then /^the response status code should be (?
P<code>d+)$/

Checks that current page response status is equal to
specified

Then /^I should see "(?P<text>(?:[^"]|")*)"$/

Checks that page contains specified text

Then /^I should not see "(?P<text>(?:[^"]|")*)"$/

Checks that page doesn't contain specified text

Then /^the response should contain "(?P<text>(?:
[^"]|")*)"$/

Checks that HTML response contains specified string

Then /^the response should not contain "(?P<text>(?:
[^"]|")*)"$/

Checks that HTML response doesn't contain specified
string

Then /^print last response$/

Prints last response to console

Then /^show last response$/

Opens last response content in browser

To list all available: bin/behat -dl
Take Advantage of MinkContext
...for features that require web acceptance testing
<?php
namespace AcmeDemoBundleFeaturesContext;
use SymfonyComponentHttpKernelKernelInterface;
use BehatSymfony2ExtensionContextKernelAwareInterface;
use BehatMinkExtensionContextMinkContext;
use BehatBehatContextBehatContext,
BehatBehatExceptionPendingException;
use BehatGherkinNodePyStringNode,
BehatGherkinNodeTableNode;
/**
* Feature context.
*/
class FeatureContext extends BehatContext //MinkContext for web test
implements KernelAwareInterface
{
private $kernel;
private $parameters;
}
Take Advantage of MinkContext
...for features that require web acceptance testing
<?php
namespace AcmeDemoBundleFeaturesContext;
use SymfonyComponentHttpKernelKernelInterface;
use BehatSymfony2ExtensionContextKernelAwareInterface;
use BehatMinkExtensionContextMinkContext;
use BehatBehatContextBehatContext,
BehatBehatExceptionPendingException;
use BehatGherkinNodePyStringNode,
BehatGherkinNodeTableNode;
/**
* Feature context.
*/
class FeatureContext extends MinkContext
implements KernelAwareInterface
{
private $kernel;
private $parameters;
}
Let's Execute Our Feature With Behat
$ ben/behat @AcmeDemoBundle --env=test
Feature: Contact form
In order to contact an email address
As a visitor
I need to be able to submit a contact form

Scenario: Successfully submit the contact form
Given I am on "/demo/contact"

# contact.feature:6
# FeatureContext::visit()

When I fill in "Email" with "user@example.com" # FeatureContext::fillField()
Form field with id|name|label|value "Email" not found
And I fill in "Message" with "Hello there!"

# FeatureContext::fillField()

And I press "Send"

# FeatureContext::pressButton()

Then I should see "Message sent!"

# FeatureContext::assertPageContainsText()

1 scenario (1 failed)
5 steps (1 passed, 3 skipped, 1 failed)
Let's Execute Our Feature With Behat
$ app/console behat @AcmeDemoBundle --env=test
Feature: Contact form
In order to contact an email address
As a visitor
I need to be able to submit a contact form

Scenario: Successfully submit the contact form

# contact.feature:6

Of course it fails. We haven't written any code yet!

Given I am on "/demo/contact"

# FeatureContext::visit()

When I fill in "Email" with "user@example.com" # FeatureContext::fillField()

This is the red step of TDD.

Form field with id|name|label|value "Email" not found
And I fill in "Message" with "Hello there!"

# FeatureContext::fillField()

And I press "Send"

# FeatureContext::pressButton()

Then I should see "Message sent!"

# FeatureContext::assertPageContainsText()

1 scenario (1 failed)
5 steps (1 passed, 3 skipped, 1 failed)
A Note About Step Results
...of which there are seven
●

●

●

●

Success: a definition was found and executing it did not throw an
Exception
Undefined: a definition couldn't be found; all subsequent steps will be
skipped
Pending: the definition threw the special PendingException, which
means you have work to do; skip remaining steps
Failure: a definition throws an Exception; Behat will skip remaining
steps and terminate with exit status 1
●

Behat can easily use PHPUnit's assertion framework or you can roll your own

●

Skipped: steps which were never executed

●

Ambiguous: multiple definitions matched a step

●

Redundant: multiple definitions share the same pattern
Implement the Contact Form
Implement the Contact Form
Implement the Contact Form
Let's Try That Again
$ app/console behat @AcmeDemoBundle --env=test
Feature: Contact form
In order to contact an email address
As a visitor
I need to be able to submit a contact form

Scenario: Successfully submit the contact form
Given I am on "/demo/contact"

# contact.feature:6
# FeatureContext::visit()

When I fill in "Email" with "user@example.com" # FeatureContext::fillField()
And I fill in "Message" with "Hello there!"

# FeatureContext::fillField()

And I press "Send"

# FeatureContext::pressButton()

Then I should see "Message sent!"

# FeatureContext::assertPageContainsText()

1 scenario (1 passed)
5 steps (5 passed)
Let's Try That Again
$ app/console behat @AcmeDemoBundle --env=test
Feature: Contact form
In order to contact an email address
As a visitor
I need to be able to submit a contact form

Scenario: Successfully submit the contact form

# contact.feature:6
Great! Our tests pass.

Given I am on "/demo/contact"

# FeatureContext::visit()

When I fill in "Email" with "user@example.com" # FeatureContext::fillField()

This is the green # FeatureContext::fillField()
step of TDD.

And I fill in "Message" with "Hello there!"
And I press "Send"

# FeatureContext::pressButton()

Then I should see "Message sent!"

# FeatureContext::assertPageContainsText()

1 scenario (1 passed)
5 steps (5 passed)
What Else Can Mink Do?
●

Provide a single API for browser behavior
●

HTTP authentication, cookies, headers, sessions

●

Page examination via XPath or CSS selectors

●

●

Page manipulation (e.g. complete forms, click, hover, dragand-drop)

Existing drivers can be used interchangeably
●

Symfony2 test client – simulated request serving

●

Goutte – headless, PHP web scraper

●

Sahi – brower-control toolkit (necessary for JS)

●

PhantomJS – headless browser-control for Webkit
–

http://www.ryangrenz.com/2011/05/30/experiments-with-behat-part1-mink-sahi-phantomjs/
Thanks!
http://behat.org/
http://mink.behat.org/
http://github.com/Behat

Jermey Mikola (@jmikola) for the slides

Contenu connexe

Dernier

Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 

Dernier (20)

Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 

En vedette

PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at WorkGetSmarter
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...DevGAMM Conference
 
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationBarbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationErica Santiago
 

En vedette (20)

PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike RoutesMore than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
 
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationBarbie - Brand Strategy Presentation
Barbie - Brand Strategy Presentation
 

BDD with Behat and symfony 2.4

  • 1. Behavior Driven Development (BDD) Iwan van Staveren (@istaveren) This presentation is of the author and does not necessarily reflect the view of Oracle
  • 2. In order to verify application behavior As a software developer I need tests
  • 3. In order to verify application behavior As a software developer I need tests Preferably automated tests
  • 4. Test-Driven Development ...is an iterative design process ● Write a test
  • 5. Test-Driven Development ...is an iterative design process ● Write a test ● Ensure the new test fails
  • 6. Test-Driven Development ...is an iterative design process ● Write a test ● Ensure the new test fails ● Write code to satisfy the test
  • 7. Test-Driven Development ...is an iterative design process ● Write a test ● Ensure the new test fails ● Write code to satisfy the test ● Ensure all tests pass
  • 8. Test-Driven Development ...is an iterative design process ● Write a test ● Ensure the new test fails ● Write code to satisfy the test ● Ensure all tests pass ● Refactor
  • 9. Dan North Introduces BDD “ I had a problem. While using and teaching agile practices like test-driven development (TDD) on projects in different environments, I kept coming across the same confusion and misunderstandings. Programmers wanted to know: ● Where to start ● What to test and what not to test ● How much to test in one go ● What to call their tests ● How to understand why a test fails http://dannorth.net/introducing-bdd/
  • 10. Dan North Introduces BDD “ I started using the word “behavior” in place of “test” in my dealings with TDD and… I now had answers to some of those TDD questions: ● ● ● What to call your test is easy – it’s a sentence describing the next behavior in which you are interested. How much to test becomes moot – you can only describe so much behavior in a single sentence. When a test fails, simply work through the process described above – either you introduced a bug, the behavior moved, or the test is no longer relevant. http://dannorth.net/introducing-bdd/
  • 11. Behavior-Driven Development ...builds upon TDD ● Write test cases in a natural language ● ● ● Understood by developers and business folks alike Helps relate domain language of requirements to the code Do this with user stories and scenarios ● User stories describe a feature's benefit in context ● Scenarios are executable acceptance criteria “ A story’s behavior is simply its acceptance criteria – if the system fulfills all the acceptance criteria, it’s behaving correctly; if it doesn’t, it isn’t. http://dannorth.net/introducing-bdd/
  • 12. So what does this look like?
  • 13. Example: A Contact Form contact.feature Feature: Contact form In order to contact an email address As a visitor I need to be able to submit a contact form Scenario: Successfully submit the contact form Given I am on "/demo/contact" When I fill in "Email" with "user@example.com" And I fill in "Message" with "Hello there!" And I press "Send" Then I should see "Message sent!"
  • 14. Example: A Contact Form contact.feature Feature: Contact form Benefit In order to contact an email address Role As a visitor Feature I need to be able to submit a contact form Scenario: Successfully submit the contact form Context Given I am on "/demo/contact" When I fill in "Email" with "user@example.com" Events And I fill in "Message" with "Hello there!" And I press "Send" Outcome Then I should see "Message sent!"
  • 15. This is where Behat and Mink come in.
  • 16. Acceptance testing (any tests) Tests a feature by executing its scenarios' steps in a context. This is where Behat and Mink come in. Web acceptance testing (functional tests) Drivers for Goutte, Sahi and Symfony2's test client.
  • 17. Add Behat to Symony composer.json add "require": { ... "behat/symfony2-extension": "*", "behat/mink-extension": "*", "behat/mink-browserkit-driver": "*" } Run composer update Create behat.yml default: # ... extensions: BehatSymfony2ExtensionExtension: mink_driver: true BehatMinkExtensionExtension: default_session: 'symfony2'
  • 18. Initialize a Bundle with Behat $ bin/behat --init @AcmeDemoBundle +d src/Acme/DemoBundle/Features - place your *.feature files here +f src/Acme/DemoBundle/Features/Context/FeatureContext.php - place your feature related code here ● ● We now have a directory to hold AcmeDemoBundle's features Behat also creates an empty FeatureContext class, which extends BehatBundle's BehatContext ● Features describe our behavior, but the context tells Behat how to evaluate our feature as an executable test
  • 19. Let's Have Behat Analyze Our Feature $ app/console behat src/Acme/DemoBundle/Features/contact.feature Feature: Contact form In order to contact an email address As a visitor I need to be able to submit a contact form Scenario: Successfully submit the contact form Given I am on "/demo/contact" When I fill in "Email" with "user@example.com" And I fill in "Message" with "Hello there!" And I press "Send" Then I should see "Message sent!" 1 scenario (1 undefined) 5 steps (5 undefined) # contact.feature:6
  • 20. Behat Creates the Glue ...but the rest is up to you You can implement step definitions for undefined steps with these snippets: /** * @Given /^I am on "([^"]*)"$/ */ public function iAmOn($arg1) { throw new PendingException(); } /** * @When /^I fill in "([^"]*)" with "([^"]*)"$/ */ public function iFillInWith($arg1, $arg2) { throw new PendingException(); } /** * @Given /^I press "([^"]*)"$/ */ public function iPress($arg1) { throw new PendingException(); } /** * @Then /^I should see "([^"]*)"$/ */ public function iShouldSee($arg1) { throw new PendingException(); }
  • 21. Not so fast. What about Mink?
  • 22. MinkContext Defines Steps ...for making requests Pattern Description Given /^I am on "(?P<page>[^"]+)"$/ Opens specified page When /^I go to "(?P<page>[^"]+)"$/ Opens specified page When /^I reload the page$/ Reloads current page When /^I move backward one page$/ Moves backward one page in history When /^I move forward one page$/ Moves forward one page in history When /^I press "(?P<button>(?:[^"]|")*)"$/ Presses button with specified id|name|title|alt|value When /^I follow "(?P<link>(?:[^"]|")*)"$/ Clicks link with specified id|title|alt|text
  • 23. MinkContext Defines Steps ...for interacting with forms Pattern Description When /^I fill in "(?P<field>(?:[^"]|")*)" with "(? P<value>(?:[^"]|")*)"$/ Fills in form field with specified id|name|label|value When /^I fill in "(?P<value>(?:[^"]|")*)" for "(? P<field>(?:[^"]|")*)"$/ Fills in form field with specified id|name|label|value When /^I fill in the following:$/ Fills in form fields with provided table When /^I select "(?P<option>(?:[^"]|")*)" from "(? P<select>(?:[^"]|")*)"$/ Selects option in select field with specified id|name| label|value When /^I check "(?P<option>(?:[^"]|")*)"$/ Checks checkbox with specified id|name|label|value When /^I uncheck "(?P<option>(?:[^"]|")*)"$/ Unchecks checkbox with specified id|name|label| value When /^I attach the file "(?P<path>[^"]*)" to "(? P<field>(?:[^"]|")*)"$/ Attaches file to field with specified id|name|label| value
  • 24. MinkContext Defines Steps ...for interacting with forms Pattern Description When /^I fill in "(?P<field>(?:[^"]|")*)" with "(? P<value>(?:[^"]|")*)"$/ Fills in form field with specified id|name|label|value When /^I fill in "(?P<value>(?:[^"]|")*)" for "(? P<field>(?:[^"]|")*)"$/ Fills in form field with specified id|name|label|value When /^I fill in the following:$/ Fills in form fields with provided table When /^I select "(?P<option>(?:[^"]|")*)" from "(? P<select>(?:[^"]|")*)"$/ Selects option in select field with specified id|name| label|value When /^I check "(?P<option>(?:[^"]|")*)"$/ Checks checkbox with specified id|name|label|value When /^I uncheck "(?P<option>(?:[^"]|")*)"$/ Unchecks checkbox with specified id|name|label| value When /^I attach the file "(?P<path>[^"]*)" to "(? P<field>(?:[^"]|")*)"$/ Attaches file to field with specified id|name|label| value What's missing here? Gherkin, the DSL Behat uses to define behaviors, specifies two multi-line argument types: tables and pystrings http://docs.behat.org/guides/1.gherkin.html#multiline-arguments When I fill in the following: | email | user@example.com | | message | Hello There! | Given lorem ipsum: """ This can be a multi-line string """
  • 25. MinkContext Defines Steps ...for querying the DOM Pattern Description Then /^I should see "(?P<text>(?:[^"]|")*)" in the "(? P<element>[^"]*)" element$/ Checks that element with specified CSS contains specified text Then /^the "(?P<element>[^"]*)" element should contain "(?P<value>(?:[^"]|")*)"$/ Checks that element with specified CSS contains specified HTML Then /^I should see an? "(?P<element>[^"]*)" element$/ Checks that element with specified CSS exists on page Then /^I should not see an? "(?P<element>[^"]*)" element$/ Checks that element with specified CSS doesn't exist on page Then /^the "(?P<field>(?:[^"]|")*)" field should contain "(?P<value>(?:[^"]|")*)"$/ Checks that form field with specified id|name|label| value has specified value Then /^the "(?P<field>(?:[^"]|")*)" field should not contain "(?P<value>(?:[^"]|")*)"$/ Checks that form field with specified id|name|label| value doesn't have specified value Then /^the "(?P<checkbox>(?:[^"]|")*)" checkbox should be checked$/ Checks that checkbox with specified id|name|label| value is checked Then /^the "(?P<checkbox>(?:[^"]|")*)" checkbox should not be checked$/ Checks that checkbox with specified id|name|label| value is unchecked
  • 26. MinkContext Defines Steps ...for examining responses Pattern Description Then /^I should be on "(?P<page>[^"]+)"$/ Checks that current page path is equal to specified Then /^the url should match "(?P<pattern>(?: [^"]|")*)"$/ Checks that current page path matches pattern Then /^the response status code should be (? P<code>d+)$/ Checks that current page response status is equal to specified Then /^I should see "(?P<text>(?:[^"]|")*)"$/ Checks that page contains specified text Then /^I should not see "(?P<text>(?:[^"]|")*)"$/ Checks that page doesn't contain specified text Then /^the response should contain "(?P<text>(?: [^"]|")*)"$/ Checks that HTML response contains specified string Then /^the response should not contain "(?P<text>(?: [^"]|")*)"$/ Checks that HTML response doesn't contain specified string Then /^print last response$/ Prints last response to console Then /^show last response$/ Opens last response content in browser To list all available: bin/behat -dl
  • 27. Take Advantage of MinkContext ...for features that require web acceptance testing <?php namespace AcmeDemoBundleFeaturesContext; use SymfonyComponentHttpKernelKernelInterface; use BehatSymfony2ExtensionContextKernelAwareInterface; use BehatMinkExtensionContextMinkContext; use BehatBehatContextBehatContext, BehatBehatExceptionPendingException; use BehatGherkinNodePyStringNode, BehatGherkinNodeTableNode; /** * Feature context. */ class FeatureContext extends BehatContext //MinkContext for web test implements KernelAwareInterface { private $kernel; private $parameters; }
  • 28. Take Advantage of MinkContext ...for features that require web acceptance testing <?php namespace AcmeDemoBundleFeaturesContext; use SymfonyComponentHttpKernelKernelInterface; use BehatSymfony2ExtensionContextKernelAwareInterface; use BehatMinkExtensionContextMinkContext; use BehatBehatContextBehatContext, BehatBehatExceptionPendingException; use BehatGherkinNodePyStringNode, BehatGherkinNodeTableNode; /** * Feature context. */ class FeatureContext extends MinkContext implements KernelAwareInterface { private $kernel; private $parameters; }
  • 29. Let's Execute Our Feature With Behat $ ben/behat @AcmeDemoBundle --env=test Feature: Contact form In order to contact an email address As a visitor I need to be able to submit a contact form Scenario: Successfully submit the contact form Given I am on "/demo/contact" # contact.feature:6 # FeatureContext::visit() When I fill in "Email" with "user@example.com" # FeatureContext::fillField() Form field with id|name|label|value "Email" not found And I fill in "Message" with "Hello there!" # FeatureContext::fillField() And I press "Send" # FeatureContext::pressButton() Then I should see "Message sent!" # FeatureContext::assertPageContainsText() 1 scenario (1 failed) 5 steps (1 passed, 3 skipped, 1 failed)
  • 30. Let's Execute Our Feature With Behat $ app/console behat @AcmeDemoBundle --env=test Feature: Contact form In order to contact an email address As a visitor I need to be able to submit a contact form Scenario: Successfully submit the contact form # contact.feature:6 Of course it fails. We haven't written any code yet! Given I am on "/demo/contact" # FeatureContext::visit() When I fill in "Email" with "user@example.com" # FeatureContext::fillField() This is the red step of TDD. Form field with id|name|label|value "Email" not found And I fill in "Message" with "Hello there!" # FeatureContext::fillField() And I press "Send" # FeatureContext::pressButton() Then I should see "Message sent!" # FeatureContext::assertPageContainsText() 1 scenario (1 failed) 5 steps (1 passed, 3 skipped, 1 failed)
  • 31. A Note About Step Results ...of which there are seven ● ● ● ● Success: a definition was found and executing it did not throw an Exception Undefined: a definition couldn't be found; all subsequent steps will be skipped Pending: the definition threw the special PendingException, which means you have work to do; skip remaining steps Failure: a definition throws an Exception; Behat will skip remaining steps and terminate with exit status 1 ● Behat can easily use PHPUnit's assertion framework or you can roll your own ● Skipped: steps which were never executed ● Ambiguous: multiple definitions matched a step ● Redundant: multiple definitions share the same pattern
  • 35. Let's Try That Again $ app/console behat @AcmeDemoBundle --env=test Feature: Contact form In order to contact an email address As a visitor I need to be able to submit a contact form Scenario: Successfully submit the contact form Given I am on "/demo/contact" # contact.feature:6 # FeatureContext::visit() When I fill in "Email" with "user@example.com" # FeatureContext::fillField() And I fill in "Message" with "Hello there!" # FeatureContext::fillField() And I press "Send" # FeatureContext::pressButton() Then I should see "Message sent!" # FeatureContext::assertPageContainsText() 1 scenario (1 passed) 5 steps (5 passed)
  • 36. Let's Try That Again $ app/console behat @AcmeDemoBundle --env=test Feature: Contact form In order to contact an email address As a visitor I need to be able to submit a contact form Scenario: Successfully submit the contact form # contact.feature:6 Great! Our tests pass. Given I am on "/demo/contact" # FeatureContext::visit() When I fill in "Email" with "user@example.com" # FeatureContext::fillField() This is the green # FeatureContext::fillField() step of TDD. And I fill in "Message" with "Hello there!" And I press "Send" # FeatureContext::pressButton() Then I should see "Message sent!" # FeatureContext::assertPageContainsText() 1 scenario (1 passed) 5 steps (5 passed)
  • 37. What Else Can Mink Do? ● Provide a single API for browser behavior ● HTTP authentication, cookies, headers, sessions ● Page examination via XPath or CSS selectors ● ● Page manipulation (e.g. complete forms, click, hover, dragand-drop) Existing drivers can be used interchangeably ● Symfony2 test client – simulated request serving ● Goutte – headless, PHP web scraper ● Sahi – brower-control toolkit (necessary for JS) ● PhantomJS – headless browser-control for Webkit – http://www.ryangrenz.com/2011/05/30/experiments-with-behat-part1-mink-sahi-phantomjs/