4. Software delivery
customer wants A
project manager understand B
developers write down C
developers effectively write down D
customer realize to get E
13. Behaviour-driven development
is a software development process based on test-driven development
(TDD). Behavior-driven development combines the general techniques
and principles of TDD with ideas from domain-driven design and object-
oriented analysis and design to provide software development and
management teams with shared tools and a shared process to
collaborate on software development.
from wikipedia
14. BDD
Although BDD is principally an idea about how software development
should be managed by both business interests and technical insight
from wikipedia
15. Dan North: What is a story?
Behaviour-driven development is an “outside-in” methodology. It starts
at the outside by identifying business outcomes, and then drills down
into the feature set that will achieve those outcomes. Each feature is
captured as a “story”, which defines the scope of the feature along with
its acceptance criteria. This article introduces the BDD approach to
defining and identifying stories and their acceptance criteria.
Read the article here
16. BDD is more than tools, but we
are here in order to use BDD
methodology in our Symfony2
projects
18. Pay attention on Behat version
The latest is 3.0 (at today) but there are so many examples and
docs for 2.5
We will work with 3.0
19. Here is a story
Feature: a description of this feature
Scenario: A business situation
Given a precondition
And another precondition
When an actor execute an action
And another action appends
Then some testable result is in place
And another condition should be verified
Scenario: Another business scenario starts
20. Here is a story in Italian
Funzionalità: una generica descrizione di questa funzionalità
Scenario: una situazione di business
Data una precondizione
E ancora una nuova precondizione
Quando un attore esegue una azione
E succede anche un altra azione
Allora esite un risultato testabile
Ed un nuova condizione da verificare
Scenario: Un altro scenario di business da verificare
21. Gherkin is a Business Readable
and Domain Specific Language
that let us describe software’s
behaviour without detailing
how that behaviour is
implemented.
From the cucumber project
22. Gherkin supports so many
languages
We are building a common language with
different actors involved into to design
process
23. There are at least two ways to use behat in
your Symfony2 applications
Per-bundle features
Specific features per bundle
Application features
All features in a single folder
25. Mainly because it is so difficult to share the
application bundle strategies to a
customer
Keep the focus on the communication not
how we implement the solution
29. Every step is a custom method in the
FeatureContext
/** @Given /^I am on "([^"]*)"$/ */
public function goOnPage($url)
{
...
}
30. Mink carries different predefined steps
I am on "a page"
I press "Get me in!"
I should see "Ok, you are in"
and more... (CSS selectors etc.)
31. But remember that we are building a
common communication domain
Write down a new step that use a mink's steps instead force a new
definition of something
34. If we run it
Feature: An hello world feature example
Scenario Outline: Say a better hello to special guests
When I am on <page>
Then I should see <hello>
Examples:
| page | hello |
| "/hello/fabien" | "Captain on the bridge" |
The text "Captain on the bridge" was not found
anywhere in the text of the current page. (BehatMinkExceptionResponseTextException)
| "/hello/walter" | "Hello Walter" |
| "/hello/marco" | "Hello Marco" |
| "/hello/giovanni" | "Hello Giovanni" |
| "/hello/martina" | "Hello Martina" |
‐‐‐ Failed scenarios:
features/hello.feature:24
39. My context clears all before runs
/**
* @BeforeScenario
*/
public function clearDatabase()
{
$purger = new ORMPurger($this‐>entityManager);
$purger‐>purge();
}
40. Load books for this scenario
Feature: My books features
Scenario: List my books
Given there are books
| title | author |
| Specification by Examples | Gojko Adzic |
| Bridging the communication gap | Gojko Adzic |
| The RSpec Book | David Chelimsky |
When I am on "/books"
Then I should see "Specification by Examples"
And I should see "Bridging the communication gap"
And I should see "The RSpec Book"
41. Run it!
(and get the missing step definition)
‐‐‐ HelloFeatureContext has missing steps. Define them with these snippets:
/**
* @Given there are books
*/
public function thereAreBooks(TableNode $table)
{
throw new PendingException();
}
43. Scenarios can share the same background
Feature: My books features
Background:
Given there are books
| title | author |
| Specification by Examples | Gojko Adzic |
| Bridging the communication gap | Gojko Adzic |
| The RSpec Book | David Chelimsky |
Scenario: List my books
When I am on "/books"
Then I should see "Specification by Examples"
And I should see "Bridging the communication gap"
And I should see "The RSpec Book"
46. GIVEN i am an interested user with email
/**
* @Given I am an interested user with email :arg1
*/
public function anInterestedUserWithEmail($email)
{
// shared in the feature context
$this‐>email = $email;
// Check that is an interested user and not an existing one
Assert::assertNull(
$this‐>entityManager
‐>getRepository("CorleyBaseBundle:User")‐>findOneByEmail($email)
);
}
47. WHEN i fill all personal fields
/**
* @When I fill all personal fields
*/
public function heFillsAllPersonalFields()
{
$this‐>mink‐>getSession()‐>getPage()
‐>find("css", "#corley_bundle_basebundle_user_email")
‐>setValue($this‐>email);
$this‐>mink‐>getSession()‐>getPage()
‐>find("css", "#corley_bundle_basebundle_user_name")
‐>setValue(rand(0,100000));
}
48. THEN I confirm my registration
/**
* @Then I confirm my registration
*/
public function iConfirmTheRegistration()
{
$client = $this‐>mink
‐>getSession()
‐>getDriver()
‐>getClient();
$client‐>followRedirects(false);
$this‐>mink‐>getSession()‐>getPage()
‐>find("css", "#corley_bundle_basebundle_user_submit")‐>click();
}
49. THEN I should be registered as an
unconfirmed user
/**
* @Then I should be registered as an unconfirmed user
*/
public function heShouldBeRegisteredAsAnUnconfirmedUser()
{
$this‐>entityManager‐>clear();
$entity = $this‐>entityManager
‐>getRepository("CorleyBaseBundle:User")
‐>findOneByEmail($this‐>email);
Assert::assertNotNull($entity);
Assert::assertFalse($entity‐>isConfirmed());
}
50. THEN I should receive the reg. email
/**
* @Then I should receive the registration email
*/
public function heShouldReceiveTheRegistrationEmail()
{
$driver = $this‐>mink‐>getSession()‐>getDriver();
if (!$driver instanceof KernelDriver) {
throw new RuntimeException("Only kernel drivers");
}
$profile = $driver‐>getClient()‐>getProfile();
if (false === $profile) {
throw new RuntimeException("Profiler is disabled");
}
$collector = $profile‐>getCollector('swiftmailer');
Assert::assertCount(1, $collector‐>getMessages());
}
You can wrap a "getProfiler" in a separate method
51. THEN I should be in the reserved area
/**
* @Then I should be in the reserved area
*/
public function heShouldBeInTheReservedArea()
{
$client = $this‐>mink
‐>getSession()
‐>getDriver()
‐>getClient();
$client‐>followRedirects(true);
$client‐>followRedirect();
$this‐>mink‐>assertSession()‐>pageTextContains("Hello reserved area!");
}
You can wrap the redirection in seperate, reusable steps
52. There are so many features in Behat
But, remember that the goal is to bridging the communication gap
between actors of the same project with different backgrounds
Do not use it for functional testing
(use LiipFunctionalTestBundle for that or anything else)