SlideShare a Scribd company logo
1 of 103
Download to read offline
Behat Best Practices with
Symfony
Ciaran McNulty
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Behaviour Driven
Development
Crania Ltd | @ciaranmcnulty | #symfonycon
BDD is the art of using examples in
conversations to illustrate behaviour
— Liz Keogh
Crania Ltd | @ciaranmcnulty | #symfonycon
BDD is the art of using
examples in
conversations to
illustrate behaviour
Crania Ltd | @ciaranmcnulty | #symfonycon
BDD is the art of using
examples in
conversations to
illustrate behaviour
Crania Ltd | @ciaranmcnulty | #symfonycon
BDD is the art of using
examples in
conversations to
illustrate behaviour
Crania Ltd | @ciaranmcnulty | #symfonycon
Example
2 - Something that serves to illustrate or explain a rule
— Wiktionary
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Rule:
We charge our customers sales tax at a rate of 20%
Crania Ltd | @ciaranmcnulty | #symfonycon
Rule:
We charge our customers sales tax at a rate of 20%
Example:
So, if an item is priced at $10, we charge $10
+ $2 tax for a total of $12
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Rule:
We charge our customers sales tax at a rate of 20%
Example:
So, if an item is priced at $10, we charge $10
+ $2 tax for a total of $12
Crania Ltd | @ciaranmcnulty | #symfonycon
Rule:
We charge our customers sales tax at a rate of 20%
Example:
No! If an item is priced at £10, we charge £10
and allocate £1.67 of that as sales tax
Crania Ltd | @ciaranmcnulty | #symfonycon
Capturing Examples
Action Outcome
What causes the behaviour? What is the result of the
behaviour?
Crania Ltd | @ciaranmcnulty | #symfonycon
Capturing Examples
Action Outcome
What causes the behaviour? What is the result of the
behaviour?
I buy a pair of Levi 501s I am charged £32.99
Crania Ltd | @ciaranmcnulty | #symfonycon
Why does that action
cause that outcome?
Crania Ltd | @ciaranmcnulty | #symfonycon
Capturing Examples
Context Action Outcome
What happened in
the past that affects
the behaviour?
What causes the
behaviour?
What is the result of
the behaviour?
Levi 501s are listed
at £32.99
I buy a pair of Levi
501s
I am charged £32.99
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Context Questioning
“Is there any other context which, when this event happens, will
produce a different outcome?” - Liz Keogh
Crania Ltd | @ciaranmcnulty | #symfonycon
Context Questioning
Context Action Outcome
Levi 501s are listed
at £32.99
I buy a pair of Levi
501s
I am charged £32.99
“Is there any situation where I could buy these jeans and pay a different
amount?”
Crania Ltd | @ciaranmcnulty | #symfonycon
Context Questioning
Context Action Outcome
Levi 501s are listed at
£32.99
I buy a pair of Levi 501s I am charged £32.99
Levis are on sale " ?
Purchaser is a staff
member
" ?
The jeans are damaged " ?
Crania Ltd | @ciaranmcnulty | #symfonycon
Outcome Questioning
“Given this context, when this event happens, is there another outcome
that’s important? Something we missed, perhaps?” - Liz Keogh
Crania Ltd | @ciaranmcnulty | #symfonycon
Outcome Questioning
Context Action Outcome
Levi 501s are listed
at £32.99
I buy a pair of Levi
501s
I am charged £32.99
"Aside from me being charged for the jeans, does something else
happen that we need to care about?"
Crania Ltd | @ciaranmcnulty | #symfonycon
Outcome Questioning
Context Action Outcome
Levi 501s are listed
at £32.99
I buy a pair of Levi
501s
I am charged
£32.99 ...and I get
sent some
jeans ...and the
warehouse stock
level is reduced
Crania Ltd | @ciaranmcnulty | #symfonycon
Example Mapping
Matt Wynne
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Outstanding questions
» Can we start work on this item without this answer?
» Do we need to resolve it first?
» Can the story be split along this seam?
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Example mapping
» One-on-one
» Large group
» Round-robin
Crania Ltd | @ciaranmcnulty | #symfonycon
Feature Mapping
John Ferguson Smart
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Event Storming
Alberto Brandolini
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
BDD is not about
testing
Crania Ltd | @ciaranmcnulty | #symfonycon
BDD is also not about
one-way requirement
capture
Crania Ltd | @ciaranmcnulty | #symfonycon
Validating examples
(ok it is a bit about testing,
sometimes)
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Behat
Crania Ltd | @ciaranmcnulty | #symfonycon
Feature: Scheduling a training course
As a trainer
In order to be able to cancel courses or schedule new ones
I should be able to specify a maximum and minimum class size
Rules:
- Course is proposed with size limits
- When enough enrolments happen, course is considered viable
- When maximum class size is reached, further enrolments are not allowed
Background:
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
Scenario: Course does not get enough enrolments to be viable
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
Scenario: Course gets enough enrolments to be viable
Given Alice has already enrolled on this course
When Bob enrols on this course
Then this course will be viable
Scenario: Enrolments are stopped when class size is reached
Given Alice, Bob and Charlie have already enrolled on this course
When Derek tries to enrol on this course
Then he should not be able to enrol
Crania Ltd | @ciaranmcnulty | #symfonycon
Feature: Scheduling a training course
As a trainer
In order to be able to cancel courses or schedule new ones
I should be able to specify a maximum and minimum class size
Rules:
- Course is proposed with size limits
- When enough enrolments happen, course is considered viable
- When maximum class size is reached, further enrolments are not allowed
Crania Ltd | @ciaranmcnulty | #symfonycon
Background:
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
Scenario: Course does not get enough enrolments to be viable
When only Alice enrols on this course
Then this course will not be viable
Crania Ltd | @ciaranmcnulty | #symfonycon
Background:
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
Scenario: Course does not get enough enrolments to be viable
When only Alice enrols on this course
Then this course will not be viable
Crania Ltd | @ciaranmcnulty | #symfonycon
Background:
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
Scenario: Course gets enough enrolments to be viable
Given Alice has already enrolled on this course
When Bob enrols on this course
Then this course will be viable
Crania Ltd | @ciaranmcnulty | #symfonycon
Background:
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
Scenario: Enrolments are stopped when class size is reached
Given Alice, Bob and Charlie have already enrolled on this course
When Derek tries to enrol on this course
Then he should not be able to enrol
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Gherkin:
Given a thing happens to Ciaran
PHP:
/**
* @Given a thing happens to :person
*/
public function doAThing(string $person)
{
// you have to write this
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Driving the Domain layer
» Drive PHP objects directly from scenario
» Proves domain supports business actions
» Aligns domain model with business language
» Executes quickly with few dependencies
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class FeatureContext implements Context
{
/**
* @Given :courseTitle was proposed with a class size of :min to :max people
*/
public function courseWasProposedWithAClassSizeOfToPeople(string $courseTitle, int $min, int $max)
{
$this->course = Course::propose(
$courseTitle,
ClassSize::between($min, $max)
);
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class FeatureContext implements Context
{
/** @Transform */
public function transformLearner(string $name) : Learner
{
return Learner::called($name);
}
/**
* @When only :learner enrols on this course
*/
public function learnerEnrolsOnCourse(Learner $learner)
{
$this->course->enrol($learner);
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class FeatureContext implements Context
{
/**
* @Then this course will not be viable
*/
public function thisCourseWillNotBeViable()
{
assert($this->course->isViable() == false);
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Driving the Service layer
» Configure services in test environment
» Inject services into Behat context (using an extension)
» Interact with domain model via the services
» Aligns service layer with business use cases
Crania Ltd | @ciaranmcnulty | #symfonycon
Suites
# behat.yml
default:
suites:
domain:
contexts: [ DomainContext ]
services:
contexts: [ ServiceContext ]
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class ServiceContext implements Context
{
public function __construct(CourseEnrolments $courseEnrolments)
{
$this->courseEnrolments = $courseEnrolments;
}
/**
* @Given :course was proposed with a class size of :min to :max people
*/
public function wasProposedWithAClassSizeOfToPeople(string $course, int $min, int $max)
{
$this->course = $course;
$this->courseEnrolments->propose($course, $min, $max);
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class ServiceContext implements Context
{
public function __construct(CourseEnrolments $courseEnrolments)
{
$this->courseEnrolments = $courseEnrolments;
}
/**
* @Given :course was proposed with a class size of :min to :max people
*/
public function wasProposedWithAClassSizeOfToPeople(string $course, int $min, int $max)
{
$this->courseId = $course;
$this->courseEnrolments->propose($course, $min, $max);
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Symfony extension
# behat.yml
default:
suites:
domain:
contexts:
- DomainContext
services:
contexts:
- ServiceContext:
courseEnrolments: "@cjm.training.enrolment.course_enrolments"
extensions:
BehatSymfony2Extension: ~
Crania Ltd | @ciaranmcnulty | #symfonycon
class CourseEnrolments
{
private $courses;
public function __construct(Courses $courses)
{
$this->courses = $courses;
}
public function propose(string $title, int $minimum, int $maximum)
{
$this->courses->add(
Course::propose(
$title,
ClassSize::between($minimum, $maximum)
)
);
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Infrastructure
» Using real infrastructure is slow
» Using fake infrastructure can lower confidence
» Use fake infrastructure but sync via contract tests
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class ServiceContext implements Context
{
/**
* @When (only) :learner enrols on this course
*/
public function learnerEnrolsOnThisCourse(string $learner)
{
$this->courseEnrolments->enrol($learner, $this->courseId);
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class ServiceContext implements Context
{
/**
* @Then this course will not be viable
*/
public function thisCourseWillNotBeViable()
{
assert($this->courseEnrolments->isCourseViable($this->courseId) == false);
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Domain vs Service layer
» Start by driving domain layer
» Refactor to services when confidence grows
» Drop back to domain layer when remodelling
Crania Ltd | @ciaranmcnulty | #symfonycon
Crania Ltd | @ciaranmcnulty | #symfonycon
Driving the UI layer
» Simulate a browser with behat/minkextension
» Interact with domain model via the UI
» Ensures UI supports business actions
» Slow, brittle, flakey...
» Does not constrain API
Crania Ltd | @ciaranmcnulty | #symfonycon
Don't do this
Scenario: Buying a pair of jeans
Given I am on "/products/levi-501"
When I click on "#add-form input[type=submit]"
Then "#basket ul" should contain "jeans"
Crania Ltd | @ciaranmcnulty | #symfonycon
Mink
» Browser driver abstraction
» Supports Selenium, Goutte, Browserkit
Crania Ltd | @ciaranmcnulty | #symfonycon
# behat.yml
deafult:
suites:
services:
contexts:
- ServiceContext:
courseEnrolments: "@cjm.training.enrolment.course_enrolments"
endtoend:
filters:
tags: "@endtoend"
contexts:
- EndToEndContext:
courseEnrolments: "@cjm.training.enrolment.course_enrolments"
Crania Ltd | @ciaranmcnulty | #symfonycon
Mink with Symfony
# behat.yml
extensions:
BehatSymfony2Extension: ~
BehatMinkExtension:
sessions:
symfony:
symfony2: ~
Crania Ltd | @ciaranmcnulty | #symfonycon
Mink with PSR-7
# behat.yml
extensions:
CjmBehatPsr7Extension:
app: %paths.base%/path/to/file.php
BehatMinkExtension:
sessions:
psr:
psr-7: ~
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class EndToEndContext implements RawMinkContext
{
/**
* @Given :course was proposed with a class size of :min to :max people
*/
public function wasProposedWithAClassSizeOfToPeople(string $course, int $min, int $max)
{
$this->course = $course;
$this->courseEnrolments->propose($course, $min, $max);
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class EndToEndContext implements RawMinkContext
{
/**
* @When only :learner enrols on this course
*/
public function learnerEnrolsOnCourse(string $learner)
{
$this->visitPath('/courses/' . $this->course);
$page = $this->getSession()->getPage();
$page->fillField('Your name', $learner);
$page->pressButton('Enrol');
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Given "BDD for Beginners" was proposed with a class size of 2 to 3 people
When only Alice enrols on this course
Then this course will not be viable
class EndToEndContext implements RawMinkContext
{
/**
* @Then this course will not be viable
*/
public function thisCourseWillNotBeViable()
{
$this->visitPath('/courses/'.$this->course);
$this->assertSession()->elementExists('css', '#not-viable-warning');
}
}
Crania Ltd | @ciaranmcnulty | #symfonycon
Automating a real browser
» Avoid or minimise
» Orders of magnitude slower
» Required for end-to-end with JS
» Can often be replaced with JS cucumber stack
Crania Ltd | @ciaranmcnulty | #symfonycon
# behat.yml
extensions:
BehatSymfony2Extension: ~
DMoreChromeExtensionBehatServiceContainerChromeExtension: ~
BehatMinkExtension:
sessions:
symfony:
symfony2: ~
chrome-driver:
chrome:
api_url: "http://localhost:9222"
Crania Ltd | @ciaranmcnulty | #symfonycon
chrome --disable-gpu --headless 
--remote-debugging-address=0.0.0.0 
--remote-debugging-port=9222
or
docker run -d -p 9222:9222 
--cap-add=SYS_ADMIN 
justinribeiro/chrome-headless
Crania Ltd | @ciaranmcnulty | #symfonycon
Summary
» Drive domain objects directly to explore model
» Refactor to services when model is stable
» Add minimal UI coverage
Crania Ltd | @ciaranmcnulty | #symfonycon
Future
» Autowiring
» Panther support
» Cucumberification
Crania Ltd | @ciaranmcnulty | #symfonycon
Thanks
» @ciaranmcnulty
» @PhpSpec
» @BDDLondon
» @SymfonyUK
github.com/ciaranmcnulty/behat-symfony-demo
Crania Ltd | @ciaranmcnulty | #symfonycon

More Related Content

More from CiaranMcNulty

Finding the Right Testing Tool for the Job
Finding the Right Testing Tool for the JobFinding the Right Testing Tool for the Job
Finding the Right Testing Tool for the JobCiaranMcNulty
 
Conscious Decoupling - Lone Star PHP
Conscious Decoupling - Lone Star PHPConscious Decoupling - Lone Star PHP
Conscious Decoupling - Lone Star PHPCiaranMcNulty
 
TDD with PhpSpec - Lone Star PHP 2016
TDD with PhpSpec - Lone Star PHP 2016TDD with PhpSpec - Lone Star PHP 2016
TDD with PhpSpec - Lone Star PHP 2016CiaranMcNulty
 
Fly In Style (without splashing out)
Fly In Style (without splashing out)Fly In Style (without splashing out)
Fly In Style (without splashing out)CiaranMcNulty
 
Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015CiaranMcNulty
 
Driving Design through Examples - PhpCon PL 2015
Driving Design through Examples - PhpCon PL 2015Driving Design through Examples - PhpCon PL 2015
Driving Design through Examples - PhpCon PL 2015CiaranMcNulty
 
Building a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesBuilding a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesCiaranMcNulty
 
Driving Design through Examples
Driving Design through ExamplesDriving Design through Examples
Driving Design through ExamplesCiaranMcNulty
 
Why Your Test Suite Sucks
Why Your Test Suite SucksWhy Your Test Suite Sucks
Why Your Test Suite SucksCiaranMcNulty
 
Driving Design with PhpSpec
Driving Design with PhpSpecDriving Design with PhpSpec
Driving Design with PhpSpecCiaranMcNulty
 
Using HttpKernelInterface for Painless Integration
Using HttpKernelInterface for Painless IntegrationUsing HttpKernelInterface for Painless Integration
Using HttpKernelInterface for Painless IntegrationCiaranMcNulty
 

More from CiaranMcNulty (12)

Finding the Right Testing Tool for the Job
Finding the Right Testing Tool for the JobFinding the Right Testing Tool for the Job
Finding the Right Testing Tool for the Job
 
Conscious Decoupling - Lone Star PHP
Conscious Decoupling - Lone Star PHPConscious Decoupling - Lone Star PHP
Conscious Decoupling - Lone Star PHP
 
TDD with PhpSpec - Lone Star PHP 2016
TDD with PhpSpec - Lone Star PHP 2016TDD with PhpSpec - Lone Star PHP 2016
TDD with PhpSpec - Lone Star PHP 2016
 
Fly In Style (without splashing out)
Fly In Style (without splashing out)Fly In Style (without splashing out)
Fly In Style (without splashing out)
 
Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015Why Your Test Suite Sucks - PHPCon PL 2015
Why Your Test Suite Sucks - PHPCon PL 2015
 
Driving Design through Examples - PhpCon PL 2015
Driving Design through Examples - PhpCon PL 2015Driving Design through Examples - PhpCon PL 2015
Driving Design through Examples - PhpCon PL 2015
 
Building a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesBuilding a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing Strategies
 
TDD with PhpSpec
TDD with PhpSpecTDD with PhpSpec
TDD with PhpSpec
 
Driving Design through Examples
Driving Design through ExamplesDriving Design through Examples
Driving Design through Examples
 
Why Your Test Suite Sucks
Why Your Test Suite SucksWhy Your Test Suite Sucks
Why Your Test Suite Sucks
 
Driving Design with PhpSpec
Driving Design with PhpSpecDriving Design with PhpSpec
Driving Design with PhpSpec
 
Using HttpKernelInterface for Painless Integration
Using HttpKernelInterface for Painless IntegrationUsing HttpKernelInterface for Painless Integration
Using HttpKernelInterface for Painless Integration
 

Recently uploaded

办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
cpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptcpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptrcbcrtm
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 

Recently uploaded (20)

办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Odoo Development Company in India | Devintelle Consulting Service
Odoo Development Company in India | Devintelle Consulting ServiceOdoo Development Company in India | Devintelle Consulting Service
Odoo Development Company in India | Devintelle Consulting Service
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
cpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptcpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.ppt
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 

Behat Best Practices with Symfony

  • 1. Behat Best Practices with Symfony Ciaran McNulty Crania Ltd | @ciaranmcnulty | #symfonycon
  • 2. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 3. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 4. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 5. Behaviour Driven Development Crania Ltd | @ciaranmcnulty | #symfonycon
  • 6. BDD is the art of using examples in conversations to illustrate behaviour — Liz Keogh Crania Ltd | @ciaranmcnulty | #symfonycon
  • 7. BDD is the art of using examples in conversations to illustrate behaviour Crania Ltd | @ciaranmcnulty | #symfonycon
  • 8. BDD is the art of using examples in conversations to illustrate behaviour Crania Ltd | @ciaranmcnulty | #symfonycon
  • 9. BDD is the art of using examples in conversations to illustrate behaviour Crania Ltd | @ciaranmcnulty | #symfonycon
  • 10. Example 2 - Something that serves to illustrate or explain a rule — Wiktionary Crania Ltd | @ciaranmcnulty | #symfonycon
  • 11. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 12. Rule: We charge our customers sales tax at a rate of 20% Crania Ltd | @ciaranmcnulty | #symfonycon
  • 13. Rule: We charge our customers sales tax at a rate of 20% Example: So, if an item is priced at $10, we charge $10 + $2 tax for a total of $12 Crania Ltd | @ciaranmcnulty | #symfonycon
  • 14. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 15. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 16. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 17. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 18. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 19. Rule: We charge our customers sales tax at a rate of 20% Example: So, if an item is priced at $10, we charge $10 + $2 tax for a total of $12 Crania Ltd | @ciaranmcnulty | #symfonycon
  • 20. Rule: We charge our customers sales tax at a rate of 20% Example: No! If an item is priced at £10, we charge £10 and allocate £1.67 of that as sales tax Crania Ltd | @ciaranmcnulty | #symfonycon
  • 21. Capturing Examples Action Outcome What causes the behaviour? What is the result of the behaviour? Crania Ltd | @ciaranmcnulty | #symfonycon
  • 22. Capturing Examples Action Outcome What causes the behaviour? What is the result of the behaviour? I buy a pair of Levi 501s I am charged £32.99 Crania Ltd | @ciaranmcnulty | #symfonycon
  • 23. Why does that action cause that outcome? Crania Ltd | @ciaranmcnulty | #symfonycon
  • 24. Capturing Examples Context Action Outcome What happened in the past that affects the behaviour? What causes the behaviour? What is the result of the behaviour? Levi 501s are listed at £32.99 I buy a pair of Levi 501s I am charged £32.99 Crania Ltd | @ciaranmcnulty | #symfonycon
  • 25. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 26. Context Questioning “Is there any other context which, when this event happens, will produce a different outcome?” - Liz Keogh Crania Ltd | @ciaranmcnulty | #symfonycon
  • 27. Context Questioning Context Action Outcome Levi 501s are listed at £32.99 I buy a pair of Levi 501s I am charged £32.99 “Is there any situation where I could buy these jeans and pay a different amount?” Crania Ltd | @ciaranmcnulty | #symfonycon
  • 28. Context Questioning Context Action Outcome Levi 501s are listed at £32.99 I buy a pair of Levi 501s I am charged £32.99 Levis are on sale " ? Purchaser is a staff member " ? The jeans are damaged " ? Crania Ltd | @ciaranmcnulty | #symfonycon
  • 29. Outcome Questioning “Given this context, when this event happens, is there another outcome that’s important? Something we missed, perhaps?” - Liz Keogh Crania Ltd | @ciaranmcnulty | #symfonycon
  • 30. Outcome Questioning Context Action Outcome Levi 501s are listed at £32.99 I buy a pair of Levi 501s I am charged £32.99 "Aside from me being charged for the jeans, does something else happen that we need to care about?" Crania Ltd | @ciaranmcnulty | #symfonycon
  • 31. Outcome Questioning Context Action Outcome Levi 501s are listed at £32.99 I buy a pair of Levi 501s I am charged £32.99 ...and I get sent some jeans ...and the warehouse stock level is reduced Crania Ltd | @ciaranmcnulty | #symfonycon
  • 32. Example Mapping Matt Wynne Crania Ltd | @ciaranmcnulty | #symfonycon
  • 33. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 34. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 35. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 36. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 37. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 38. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 39. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 40. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 41. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 42. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 43. Outstanding questions » Can we start work on this item without this answer? » Do we need to resolve it first? » Can the story be split along this seam? Crania Ltd | @ciaranmcnulty | #symfonycon
  • 44. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 45. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 46. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 47. Example mapping » One-on-one » Large group » Round-robin Crania Ltd | @ciaranmcnulty | #symfonycon
  • 48. Feature Mapping John Ferguson Smart Crania Ltd | @ciaranmcnulty | #symfonycon
  • 49. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 50. Event Storming Alberto Brandolini Crania Ltd | @ciaranmcnulty | #symfonycon
  • 51. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 52. BDD is not about testing Crania Ltd | @ciaranmcnulty | #symfonycon
  • 53. BDD is also not about one-way requirement capture Crania Ltd | @ciaranmcnulty | #symfonycon
  • 54. Validating examples (ok it is a bit about testing, sometimes) Crania Ltd | @ciaranmcnulty | #symfonycon
  • 55. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 56. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 57. Behat Crania Ltd | @ciaranmcnulty | #symfonycon
  • 58. Feature: Scheduling a training course As a trainer In order to be able to cancel courses or schedule new ones I should be able to specify a maximum and minimum class size Rules: - Course is proposed with size limits - When enough enrolments happen, course is considered viable - When maximum class size is reached, further enrolments are not allowed Background: Given "BDD for Beginners" was proposed with a class size of 2 to 3 people Scenario: Course does not get enough enrolments to be viable Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable Scenario: Course gets enough enrolments to be viable Given Alice has already enrolled on this course When Bob enrols on this course Then this course will be viable Scenario: Enrolments are stopped when class size is reached Given Alice, Bob and Charlie have already enrolled on this course When Derek tries to enrol on this course Then he should not be able to enrol Crania Ltd | @ciaranmcnulty | #symfonycon
  • 59. Feature: Scheduling a training course As a trainer In order to be able to cancel courses or schedule new ones I should be able to specify a maximum and minimum class size Rules: - Course is proposed with size limits - When enough enrolments happen, course is considered viable - When maximum class size is reached, further enrolments are not allowed Crania Ltd | @ciaranmcnulty | #symfonycon
  • 60. Background: Given "BDD for Beginners" was proposed with a class size of 2 to 3 people Scenario: Course does not get enough enrolments to be viable When only Alice enrols on this course Then this course will not be viable Crania Ltd | @ciaranmcnulty | #symfonycon
  • 61. Background: Given "BDD for Beginners" was proposed with a class size of 2 to 3 people Scenario: Course does not get enough enrolments to be viable When only Alice enrols on this course Then this course will not be viable Crania Ltd | @ciaranmcnulty | #symfonycon
  • 62. Background: Given "BDD for Beginners" was proposed with a class size of 2 to 3 people Scenario: Course gets enough enrolments to be viable Given Alice has already enrolled on this course When Bob enrols on this course Then this course will be viable Crania Ltd | @ciaranmcnulty | #symfonycon
  • 63. Background: Given "BDD for Beginners" was proposed with a class size of 2 to 3 people Scenario: Enrolments are stopped when class size is reached Given Alice, Bob and Charlie have already enrolled on this course When Derek tries to enrol on this course Then he should not be able to enrol Crania Ltd | @ciaranmcnulty | #symfonycon
  • 64. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 65. Gherkin: Given a thing happens to Ciaran PHP: /** * @Given a thing happens to :person */ public function doAThing(string $person) { // you have to write this } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 66. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 67. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 68. Driving the Domain layer » Drive PHP objects directly from scenario » Proves domain supports business actions » Aligns domain model with business language » Executes quickly with few dependencies Crania Ltd | @ciaranmcnulty | #symfonycon
  • 69. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 70. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class FeatureContext implements Context { /** * @Given :courseTitle was proposed with a class size of :min to :max people */ public function courseWasProposedWithAClassSizeOfToPeople(string $courseTitle, int $min, int $max) { $this->course = Course::propose( $courseTitle, ClassSize::between($min, $max) ); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 71. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 72. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 73. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class FeatureContext implements Context { /** @Transform */ public function transformLearner(string $name) : Learner { return Learner::called($name); } /** * @When only :learner enrols on this course */ public function learnerEnrolsOnCourse(Learner $learner) { $this->course->enrol($learner); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 74. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class FeatureContext implements Context { /** * @Then this course will not be viable */ public function thisCourseWillNotBeViable() { assert($this->course->isViable() == false); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 75. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 76. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 77. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 78. Driving the Service layer » Configure services in test environment » Inject services into Behat context (using an extension) » Interact with domain model via the services » Aligns service layer with business use cases Crania Ltd | @ciaranmcnulty | #symfonycon
  • 79. Suites # behat.yml default: suites: domain: contexts: [ DomainContext ] services: contexts: [ ServiceContext ] Crania Ltd | @ciaranmcnulty | #symfonycon
  • 80. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class ServiceContext implements Context { public function __construct(CourseEnrolments $courseEnrolments) { $this->courseEnrolments = $courseEnrolments; } /** * @Given :course was proposed with a class size of :min to :max people */ public function wasProposedWithAClassSizeOfToPeople(string $course, int $min, int $max) { $this->course = $course; $this->courseEnrolments->propose($course, $min, $max); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 81. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class ServiceContext implements Context { public function __construct(CourseEnrolments $courseEnrolments) { $this->courseEnrolments = $courseEnrolments; } /** * @Given :course was proposed with a class size of :min to :max people */ public function wasProposedWithAClassSizeOfToPeople(string $course, int $min, int $max) { $this->courseId = $course; $this->courseEnrolments->propose($course, $min, $max); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 82. Symfony extension # behat.yml default: suites: domain: contexts: - DomainContext services: contexts: - ServiceContext: courseEnrolments: "@cjm.training.enrolment.course_enrolments" extensions: BehatSymfony2Extension: ~ Crania Ltd | @ciaranmcnulty | #symfonycon
  • 83. class CourseEnrolments { private $courses; public function __construct(Courses $courses) { $this->courses = $courses; } public function propose(string $title, int $minimum, int $maximum) { $this->courses->add( Course::propose( $title, ClassSize::between($minimum, $maximum) ) ); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 84. Infrastructure » Using real infrastructure is slow » Using fake infrastructure can lower confidence » Use fake infrastructure but sync via contract tests Crania Ltd | @ciaranmcnulty | #symfonycon
  • 85. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class ServiceContext implements Context { /** * @When (only) :learner enrols on this course */ public function learnerEnrolsOnThisCourse(string $learner) { $this->courseEnrolments->enrol($learner, $this->courseId); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 86. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class ServiceContext implements Context { /** * @Then this course will not be viable */ public function thisCourseWillNotBeViable() { assert($this->courseEnrolments->isCourseViable($this->courseId) == false); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 87. Domain vs Service layer » Start by driving domain layer » Refactor to services when confidence grows » Drop back to domain layer when remodelling Crania Ltd | @ciaranmcnulty | #symfonycon
  • 88. Crania Ltd | @ciaranmcnulty | #symfonycon
  • 89. Driving the UI layer » Simulate a browser with behat/minkextension » Interact with domain model via the UI » Ensures UI supports business actions » Slow, brittle, flakey... » Does not constrain API Crania Ltd | @ciaranmcnulty | #symfonycon
  • 90. Don't do this Scenario: Buying a pair of jeans Given I am on "/products/levi-501" When I click on "#add-form input[type=submit]" Then "#basket ul" should contain "jeans" Crania Ltd | @ciaranmcnulty | #symfonycon
  • 91. Mink » Browser driver abstraction » Supports Selenium, Goutte, Browserkit Crania Ltd | @ciaranmcnulty | #symfonycon
  • 92. # behat.yml deafult: suites: services: contexts: - ServiceContext: courseEnrolments: "@cjm.training.enrolment.course_enrolments" endtoend: filters: tags: "@endtoend" contexts: - EndToEndContext: courseEnrolments: "@cjm.training.enrolment.course_enrolments" Crania Ltd | @ciaranmcnulty | #symfonycon
  • 93. Mink with Symfony # behat.yml extensions: BehatSymfony2Extension: ~ BehatMinkExtension: sessions: symfony: symfony2: ~ Crania Ltd | @ciaranmcnulty | #symfonycon
  • 94. Mink with PSR-7 # behat.yml extensions: CjmBehatPsr7Extension: app: %paths.base%/path/to/file.php BehatMinkExtension: sessions: psr: psr-7: ~ Crania Ltd | @ciaranmcnulty | #symfonycon
  • 95. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class EndToEndContext implements RawMinkContext { /** * @Given :course was proposed with a class size of :min to :max people */ public function wasProposedWithAClassSizeOfToPeople(string $course, int $min, int $max) { $this->course = $course; $this->courseEnrolments->propose($course, $min, $max); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 96. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class EndToEndContext implements RawMinkContext { /** * @When only :learner enrols on this course */ public function learnerEnrolsOnCourse(string $learner) { $this->visitPath('/courses/' . $this->course); $page = $this->getSession()->getPage(); $page->fillField('Your name', $learner); $page->pressButton('Enrol'); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 97. Given "BDD for Beginners" was proposed with a class size of 2 to 3 people When only Alice enrols on this course Then this course will not be viable class EndToEndContext implements RawMinkContext { /** * @Then this course will not be viable */ public function thisCourseWillNotBeViable() { $this->visitPath('/courses/'.$this->course); $this->assertSession()->elementExists('css', '#not-viable-warning'); } } Crania Ltd | @ciaranmcnulty | #symfonycon
  • 98. Automating a real browser » Avoid or minimise » Orders of magnitude slower » Required for end-to-end with JS » Can often be replaced with JS cucumber stack Crania Ltd | @ciaranmcnulty | #symfonycon
  • 99. # behat.yml extensions: BehatSymfony2Extension: ~ DMoreChromeExtensionBehatServiceContainerChromeExtension: ~ BehatMinkExtension: sessions: symfony: symfony2: ~ chrome-driver: chrome: api_url: "http://localhost:9222" Crania Ltd | @ciaranmcnulty | #symfonycon
  • 100. chrome --disable-gpu --headless --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 or docker run -d -p 9222:9222 --cap-add=SYS_ADMIN justinribeiro/chrome-headless Crania Ltd | @ciaranmcnulty | #symfonycon
  • 101. Summary » Drive domain objects directly to explore model » Refactor to services when model is stable » Add minimal UI coverage Crania Ltd | @ciaranmcnulty | #symfonycon
  • 102. Future » Autowiring » Panther support » Cucumberification Crania Ltd | @ciaranmcnulty | #symfonycon
  • 103. Thanks » @ciaranmcnulty » @PhpSpec » @BDDLondon » @SymfonyUK github.com/ciaranmcnulty/behat-symfony-demo Crania Ltd | @ciaranmcnulty | #symfonycon