PHPSpec & Behat: Two Testing
Tools That Write Code For You
Presented by Joshua Warren
Slide 2
Slide 2 text
OR:
Slide 3
Slide 3 text
No content
Slide 4
Slide 4 text
I heard you like to code, so
let’s write code that writes
code while you code.
Slide 5
Slide 5 text
About Me
Slide 6
Slide 6 text
PHP
Developer
Working with PHP since
1999
Slide 7
Slide 7 text
Founder &
CEO
Founded Creatuity in 2008
PHP Development Firm
Focused on the Magento
platform
Slide 8
Slide 8 text
JoshuaWarren.com
@JoshuaSWarren
Slide 9
Slide 9 text
IMPORTANT!
• joind.in/15623
• Download slides
• Post comments
• Leave a rating!
Slide 10
Slide 10 text
What You Need To Know
ASSUMPTIONS
Slide 11
Slide 11 text
Today we assume you’re a PHP
developer.
Slide 12
Slide 12 text
That you are familiar with test driven
development.
Slide 13
Slide 13 text
And that you’ve at least tried PHPUnit,
Selenium or another testing tool.
Slide 14
Slide 14 text
BDD - no, the B does not stand for beer, despite what a Brit might
tell you
Behavior Driven
Development
Slide 15
Slide 15 text
Think of BDD as stepping up a level
from TDD.
Slide 16
Slide 16 text
Graphic thanks to BugHuntres
Slide 17
Slide 17 text
TDD generally deals with functional
units.
Slide 18
Slide 18 text
BDD steps up a level to consider complete
features.
Slide 19
Slide 19 text
In BDD, you write feature files in the form of
user stories that you test against.
Slide 20
Slide 20 text
BDD uses a ubiquitous language - basically,
a language that business stakeholders,
project managers, developers and our
automated tools can all understand.
Slide 21
Slide 21 text
Sample Behat Feature File
Feature: Up and Running
In order to confirm Behat is Working
As a developer
I need to see a homepage
Scenario: Homepage Exists
When I go to "/bdd/"
Then I should see "Welcome to the world of BDD"
Slide 22
Slide 22 text
BDD gets all stakeholders to agree on what
“done” looks like before you write a single
line of code
Slide 23
Slide 23 text
Behat
Slide 24
Slide 24 text
We implement BDD in PHP with a tool
called Behat
Slide 25
Slide 25 text
Behat is a free, open source tool
designed for BDD and PHP
Slide 26
Slide 26 text
behat.org
Slide 27
Slide 27 text
SpecBDD - aka, Testing Tongue Twisters
Specification Behavior Driven
Development
Slide 28
Slide 28 text
Before you write a line of code, you
write a specification for how that code
should work
Slide 29
Slide 29 text
Focuses you on architectural decisions up-
front
Slide 30
Slide 30 text
PHPSpec
Slide 31
Slide 31 text
Open Source tool for specification driven
development in PHP
Slide 32
Slide 32 text
www.phpspec.net
Slide 33
Slide 33 text
Why Use Behat and
PHPSpec?
Slide 34
Slide 34 text
These tools allow you to focus
exclusively on logic
Slide 35
Slide 35 text
Helps build functional testing coverage
quickly
Slide 36
Slide 36 text
Guides planning and ensuring that all
stakeholders are in agreement
Slide 37
Slide 37 text
Why Not PHPUnit?
Slide 38
Slide 38 text
PHPSpec is opinionated - in every sense of the
word
Slide 39
Slide 39 text
PHPSpec forces you to think differently and
creates a mindset that encourages usage
Slide 40
Slide 40 text
PHPSpec tests are much more readable
Slide 41
Slide 41 text
Read any of Marcello Duarte’s slides on testing
Slide 42
Slide 42 text
What About
Performance?
Slide 43
Slide 43 text
Tests that take days to run won’t be used
Slide 44
Slide 44 text
PHPSpec is fast
Slide 45
Slide 45 text
Behat supports parallel execution
Slide 46
Slide 46 text
Behat and PHPSpec will be at least as fast as
the existing testing tools, and can be much
faster
Slide 47
Slide 47 text
Enough Theory:
Let’s Build Something!
Slide 48
Slide 48 text
We’ll be building a basic time-off
request app.
Slide 49
Slide 49 text
Visitors can specify their name and a
reason for their time off request.
Slide 50
Slide 50 text
Time off requests can be viewed,
approved and denied.
Slide 51
Slide 51 text
Intentionally keeping things simple,
but you can follow this pattern to add
authentication, roles, etc.
features/bootstrap/FeatureContext.php
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\MinkExtension\Context\MinkContext;
/**
* Defines application features from the specific context.
*/
class FeatureContext extends Behat\MinkExtension\Context\MinkContext
{
}
features/UpAndRunning.feature
Feature: Up and Running
In order to confirm Behat is Working
As a developer
I need to see a homepage
Scenario: Homepage Exists
When I go to "/bdd/"
Then I should see "Welcome to the world of BDD"
Slide 72
Slide 72 text
Run:
bin/behat
Slide 73
Slide 73 text
features/SubmitTimeOffRequest.feature
Feature: Submit Time Off Request
In order to request time off
As a developer
I need to be able to fill out a time off request form
Scenario: Time Off Request Form Exists
When I go to "/bdd/timeoff/new"
Then I should see "New Time Off Request"
Scenario: Time Off Request Form Works
When I go to "/bdd/timeoff/new"
And I fill in "name" with "Josh"
And I fill in "reason" with "Attending a great conference"
And I press "submit"
Then I should see "Time Off Request Submitted"
Slide 74
Slide 74 text
features/SubmitTimeOffRequest.feature
Feature: Submit Time Off Request
In order to request time off
As a developer
I need to be able to fill out a time off request form
Scenario: Time Off Request Form Exists
When I go to "/bdd/timeoff/new"
Then I should see "New Time Off Request"
Scenario: Time Off Request Form Works
When I go to "/bdd/timeoff/new"
And I fill in "name" with "Josh"
And I fill in "reason" with "Attending a great conference"
And I press "submit"
Then I should see "Time Off Request Submitted"
Slide 75
Slide 75 text
features/SubmitTimeOffRequest.feature
Feature: Submit Time Off Request
In order to request time off
As a developer
I need to be able to fill out a time off request form
Scenario: Time Off Request Form Exists
When I go to "/bdd/timeoff/new"
Then I should see "New Time Off Request"
Scenario: Time Off Request Form Works
When I go to "/bdd/timeoff/new"
And I fill in "name" with "Josh"
And I fill in "reason" with "Attending a great conference"
And I press "submit"
Then I should see "Time Off Request Submitted"
Slide 76
Slide 76 text
features/SubmitTimeOffRequest.feature
Feature: Submit Time Off Request
In order to request time off
As a developer
I need to be able to fill out a time off request form
Scenario: Time Off Request Form Exists
When I go to "/bdd/timeoff/new"
Then I should see "New Time Off Request"
Scenario: Time Off Request Form Works
When I go to "/bdd/timeoff/new"
And I fill in "name" with "Josh"
And I fill in "reason" with "Attending a great conference"
And I press "submit"
Then I should see "Time Off Request Submitted"
Slide 77
Slide 77 text
features/ProcessTimeOffRequest.feature
Feature: Process Time Off Request
In order to manage my team
As a manager
I need to be able to approve and deny time off requests
Scenario: Time Off Request Management View Exists
When I go to "/bdd/timeoff/manage"
Then I should see "Manage Time Off Requests"
Scenario: Time Off Request List
When I go to "/bdd/timeoff/manage"
And I press "View"
Then I should see "Pending Time Off Request Details"
Scenario: Approve Time Off Request
When I go to "/bdd/timeoff/manage"
And I press "View"
And I press "Approve"
Then I should see "Time Off Request Approved"
Scenario: Deny Time Off Request
When I go to "/bdd/timeoff/manage"
And I press "View"
And I press "Deny"
Then I should see "Time Off Request Denied"
Slide 78
Slide 78 text
features/ProcessTimeOffRequest.feature
Feature: Process Time Off Request
In order to manage my team
As a manager
I need to be able to approve and deny time off
requests
Slide 79
Slide 79 text
features/ProcessTimeOffRequest.feature
Scenario: Time Off Request Management View
Exists
When I go to "/bdd/timeoff/manage"
Then I should see "Manage Time Off
Requests"
Scenario: Time Off Request List
When I go to "/bdd/timeoff/manage"
And I press "View"
Then I should see "Pending Time Off Request
Details"
Slide 80
Slide 80 text
features/ProcessTimeOffRequest.feature
Scenario: Approve Time Off Request
When I go to "/bdd/timeoff/manage"
And I press "View"
And I press "Approve"
Then I should see "Time Off Request Approved"
Scenario: Deny Time Off Request
When I go to "/bdd/timeoff/manage"
And I press "View"
And I press "Deny"
Then I should see "Time Off Request Denied"
Behat Output
Scenario: Time Off Request Management View
Exists
When I go to “/bdd/timeoff/manage"
Then I should see "Manage Time Off Requests"
The text "Manage Time Off Requests" was
not found anywhere in the text of the current
page.
Slide 84
Slide 84 text
No content
Slide 85
Slide 85 text
These failures show us that Behat is
testing our app properly, and now we
just need to write the application
logic.
Slide 86
Slide 86 text
Specifications
Slide 87
Slide 87 text
Now we write specifications for how
our application should work.
Slide 88
Slide 88 text
These specifications should provide
the logic to deliver the results that
Behat is testing for.
Slide 89
Slide 89 text
bin/phpspec describe App\\Timeoff
Slide 90
Slide 90 text
PHPSpec generates a basic spec file
for us
Slide 91
Slide 91 text
spec\TimeoffSpec.php
namespace spec\App;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class TimeoffSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('App\Timeoff');
}
}
Slide 92
Slide 92 text
This default spec tells PHPSpec to
expect a class named Timeoff.
Slide 93
Slide 93 text
Now we add a bit more to the file so
PHPSpec will understand what this
class should do.
Slide 94
Slide 94 text
spec\TimeoffSpec.php
function it_creates_timeoff_requests() {
$this->create("Name", "reason")->shouldBeString();
}
function it_loads_all_timeoff_requests() {
$this->loadAll()->shouldBeArray();
}
function it_loads_a_timeoff_request() {
$this->load("uuid")->shouldBeArray();
}
function it_loads_pending_timeoff_requests() {
$this->loadPending()->shouldBeArray();
}
function it_approves_timeoff_requests() {
$this->approve("id")->shouldReturn(true);
}
function it_denies_timeoff_requests() {
$this->deny("id")->shouldReturn(true);
}
Slide 95
Slide 95 text
spec\TimeoffSpec.php
function it_creates_timeoff_requests() {
$this->create("Name", "reason")-
>shouldBeString();
}
function it_loads_all_timeoff_requests() {
$this->loadAll()->shouldBeArray();
}
Slide 96
Slide 96 text
spec\TimeoffSpec.php
function it_loads_a_timeoff_request() {
$this->load("uuid")->shouldBeArray();
}
function it_loads_pending_timeoff_requests() {
$this->loadPending()->shouldBeArray();
}
Slide 97
Slide 97 text
spec\TimeoffSpec.php
function it_approves_timeoff_requests() {
$this->approve("id")->shouldReturn(true);
}
function it_denies_timeoff_requests() {
$this->deny("id")->shouldReturn(true);
}
Slide 98
Slide 98 text
Now we run PHPSpec once more…
Slide 99
Slide 99 text
Phpspec output
10 ✔ is initializable
15 ! creates timeoff requests
method App\Timeoff::create not found.
19 ! loads all timeoff requests
method App\Timeoff::loadAll not found.
23 ! loads pending timeoff requests
method App\Timeoff::loadPending not found.
27 ! approves timeoff requests
method App\Timeoff::approve not found.
31 ! denies timeoff requests
method App\Timeoff::deny not found.
Slide 100
Slide 100 text
Lots of failures…
Slide 101
Slide 101 text
But wait a second - PHPSpec prompts
us!
Slide 102
Slide 102 text
PHPSpec output
Do you want me to create `App
\Timeoff::create()` for you?
[Y/n]
Slide 103
Slide 103 text
PHPSpec will create the class and the
methods for us!
Slide 104
Slide 104 text
This is very powerful with frameworks like
Laravel and Magento, which have PHPSpec
plugins that help PHPSpec know where class
files should be located.
Slide 105
Slide 105 text
And now, the easy part…
Slide 106
Slide 106 text
Implementation
Slide 107
Slide 107 text
Implement logic in the new Timeoff
class in the locations directed by
PHPSpec
Slide 108
Slide 108 text
Implement each function one at a time,
running phpspec after each one.
Slide 109
Slide 109 text
phpspec should be returning all green
Slide 110
Slide 110 text
Move on to implementing the front-
end behavior
Slide 111
Slide 111 text
Using Lumen means our view/display
logic is very simple
Slide 112
Slide 112 text
app\Http\route.php
$app->get('/bdd/', function() use ($app) {
return "Welcome to the world of BDD";
});