Slide 1

Slide 1 text

@michellesanver Behat+Mink+PhantomJS = TEST ALL THE THINGS* * Maybe not all the things

Slide 2

Slide 2 text

@michellesanver

Slide 3

Slide 3 text

@michellesanver WIIIIIIIE \o/ “Learn the most by sharing your knowledge with others” - @coderabbi

Slide 4

Slide 4 text

@michellesanver Audience Do you test your application? (With automatic testing not just clicking in a browser)

Slide 5

Slide 5 text

@michellesanver I would have a picture of grumpy programmer here, but I didn’t want to pay the royalties.

Slide 6

Slide 6 text

@michellesanver “My code is so awesome, I don’t need tests.” - Conference attendee

Slide 7

Slide 7 text

@michellesanver Audience Do you use PHPUnit?

Slide 8

Slide 8 text

@michellesanver Audience TDD? BDD?

Slide 9

Slide 9 text

@michellesanver This talk is entry-level.

Slide 10

Slide 10 text

@michellesanver

Slide 11

Slide 11 text

@michellesanver

Slide 12

Slide 12 text

@michellesanver Michelle Sanver

Slide 13

Slide 13 text

@michellesanver Michelle Sanver Co-President of PHPWomen

Slide 14

Slide 14 text

@michellesanver PHPWomen 30 dollars

Slide 15

Slide 15 text

@michellesanver Michelle Sanver Accent(s)!?

Slide 16

Slide 16 text

@michellesanver Michelle Sanver

Slide 17

Slide 17 text

@michellesanver I’m a backend developer.

Slide 18

Slide 18 text

@michellesanver This talk is Open Source

Slide 19

Slide 19 text

@michellesanver What is Behat anyway?

Slide 20

Slide 20 text

@michellesanver Behat!? Behat is a Behaviour Driven Development framework

Slide 21

Slide 21 text

@michellesanver Erm, what is BDD, anyway?

Slide 22

Slide 22 text

@michellesanver BDD BDD is clearly defined behaviour and English spoken tests :D

Slide 23

Slide 23 text

@michellesanver BDD BDD = TDD + Spices

Slide 24

Slide 24 text

@michellesanver Why we chose Behat

Slide 25

Slide 25 text

@michellesanver Gherkin Defining your behaviour

Slide 26

Slide 26 text

@michellesanver Gherkin Feature: List
 In order to use the shoppinglist
 As a website user
 I need to be able to see the shoppinglist and use its elements
 
 @anonymous
 Scenario: Visit shoppinglist page as anonymous
 Given I am not logged in
 And I am on profile “shopping lists"
 Then I should see “My shopping lists"
 And I should see “I don’t have an M-connect account yet“
 
 @loggedin
 Scenario: See the shoppinglist
 Given I am logged in
 And I am on profile “shopping lists"
 Then I should see "My shopping lists"
 And I should see "I don’t have an M-connect account yet"

Slide 27

Slide 27 text

@michellesanver Gherkin: Feature Feature: List
 In order to use the shoppinglist
 As a website user
 I need to be able to see the shoppinglist and use its elements
 
 @anonymous
 Scenario: Visit shoppinglist page as anonymous
 Given I am not logged in
 And I am on profile “shopping lists"
 Then I should see “My shopping lists"
 And I should see “I don’t have an M-connect account yet“
 
 @loggedin
 Scenario: See the shoppinglist
 Given I am logged in
 And I am on profile “shopping lists"
 Then I should see “My shopping lists"
 And I should see “Active lists"

Slide 28

Slide 28 text

@michellesanver Gherkin: Scenario Feature: List
 In order to use the shoppinglist
 As a website user
 I need to be able to see the shoppinglist and use its elements
 
 Scenario: Larry garfield at a conference
 Given I am Larry Garfield
 And I am at a conference
 Then I should wear “Blue shirt”
 And I should wear “Leather vest"
 
 @loggedin
 Scenario: See the shoppinglist
 Given I am logged in
 And I am on profile “shopping lists"
 Then I should see “My shopping lists"
 And I should see “Active lists"

Slide 29

Slide 29 text

@michellesanver Gherkin Given, Whens, Thens Scenario: Larry garfield at a conference
 Given I am Larry Garfield
 And I am at a conference
 Then I should wear “Blue shirt”
 And I should wear “Leather vest"

Slide 30

Slide 30 text

@michellesanver Gherkin And + But Scenario: Larry garfield at a conference
 Given I am Larry Garfield
 And I am at a conference
 Then I should wear “Blue shirt”
 And I should wear “Leather vest"

Slide 31

Slide 31 text

@michellesanver Gherkin: Background Feature: Multiple site support
 
 Background:
 Given a global administrator named "Greg"
 And a blog named "Greg's anti-tax rants"
 And a customer named "Wilson"
 And a blog named "Expensive Therapy" owned by "Wilson"
 
 Scenario: Wilson posts to his own blog
 Given I am logged in as Wilson
 When I try to post to "Expensive Therapy"
 Then I should see "Your article was published."
 
 Scenario: Greg posts to a client's blog
 Given I am logged in as Greg
 When I try to post to "Expensive Therapy"
 Then I should see "Your article was published."

Slide 32

Slide 32 text

@michellesanver Gherkin: Scenario Outlines Scenario: Drink 5 out of 12
 Given there are 12 beers
 When I drink 5 beers
 Then I should have 7 beers
 
 Scenario: Drink 5 out of 20
 Given there are 20 beers
 When I drink 5 beers
 Then I should have 15 beers

Slide 33

Slide 33 text

@michellesanver Gherkin: Scenario Outlines Scenario Outline: Lonestar social
 Given there are beers
 When I drink beers
 Then I should have beers
 
 Examples:
 | start | drink | left |
 | 12 | 5 | 7 |
 | 20 | 5 | 15 | Keep it DRY

Slide 34

Slide 34 text

@michellesanver Gherkin Tagged Hooks (@loggedin etc that we’ve seen)

Slide 35

Slide 35 text

@michellesanver Reusable Actions: Steps

Slide 36

Slide 36 text

@michellesanver Steps Each keyword (given, when etc.) is a step

Slide 37

Slide 37 text

@michellesanver Steps Automatically add steps by accepting context!

Slide 38

Slide 38 text

@michellesanver Steps use Behat\Behat\Context\SnippetAcceptingContext;
 use Behat\Gherkin\Node\PyStringNode;
 use Behat\Gherkin\Node\TableNode;
 
 class FeatureContext implements SnippetAcceptingContext
 {
 /**
 * Initializes context.
 */
 public function __construct()
 {
 }
 }

Slide 39

Slide 39 text

@michellesanver Steps: Results Pending

Slide 40

Slide 40 text

@michellesanver Steps: Results Failed

Slide 41

Slide 41 text

@michellesanver Steps: Results Skipped

Slide 42

Slide 42 text

@michellesanver Steps: Results Ambigious

Slide 43

Slide 43 text

@michellesanver Steps: Results Reduntant

Slide 44

Slide 44 text

@michellesanver Steps: Results Undefined

Slide 45

Slide 45 text

@michellesanver SUCCESSFUL \o/ :D

Slide 46

Slide 46 text

@michellesanver Hooked on a feeling *bamdabadam* Hooks

Slide 47

Slide 47 text

@michellesanver Hooks Suite Hooks

Slide 48

Slide 48 text

@michellesanver Hooks Feature Hooks

Slide 49

Slide 49 text

@michellesanver Hooks Scenario Hooks

Slide 50

Slide 50 text

@michellesanver Hooks Step Hooks

Slide 51

Slide 51 text

@michellesanver Hooks /** 
 * @BeforeScenario
 */
 public function before(BeforeScenarioScope $scope)
 {
 if($this->getMink()->getDefaultSessionName() == 'selenium2') {
 $this->getSession()->resizeWindow(1200, 768);
 } else {
 self::$client = $this->getSession(‘symfony2') ->getDriver()->getClient();
 self::$container = self::$client->getContainer();
 self::$guzzle = self::$container->get(‘guzzle.api.client’);
 }
 }

Slide 52

Slide 52 text

@michellesanver Tagged hooks /**
 * @BeforeScenario @database,@orm
 */
 public function cleanDatabase()
 {
 // clean database before
 // @database OR @orm scenarios
 }

Slide 53

Slide 53 text

@michellesanver Tagged hooks: && /**
 * @BeforeScenario @database&&@fixtures
 */
 public function cleanDatabase()
 {
 // clean database before
 // @database OR @orm scenarios
 }

Slide 54

Slide 54 text

@michellesanver Context

Slide 55

Slide 55 text

@michellesanver Context Feature How your application behaves Context How to test your application

Slide 56

Slide 56 text

@michellesanver Context FeatureContext Class

Slide 57

Slide 57 text

@michellesanver Context /**
 * @When I do something with :argument
 */
 public function iDoSomethingWith($argument)
 {
 // do something with $argument
 }

Slide 58

Slide 58 text

@michellesanver Context Contact class requirements

Slide 59

Slide 59 text

@michellesanver Context: Class requirements 1. The context class should implement the Behat\Behat\Context \Context interface.

Slide 60

Slide 60 text

@michellesanver Context: Class requirements 2. The context class should be called FeatureContext.

Slide 61

Slide 61 text

@michellesanver Context: Class requirements 3. The context class should be discoverable and loadable by Behat.

Slide 62

Slide 62 text

@michellesanver Context Contexts lifetime

Slide 63

Slide 63 text

@michellesanver Context lifetime Your context class is initialized before each scenario is run

Slide 64

Slide 64 text

@michellesanver Context lifetime 1. Every scenario is isolated from each other scenario’s context.

Slide 65

Slide 65 text

@michellesanver Context lifetime 2. Every step in a single scenario is executed inside a common context instance.

Slide 66

Slide 66 text

@michellesanver Context Multiple contexts

Slide 67

Slide 67 text

@michellesanver Multiple contexts # behat.yml default: suites: default: contexts: - FeatureContext - SecondContext - ThirdContext

Slide 68

Slide 68 text

@michellesanver Writing TESTS

Slide 69

Slide 69 text

@michellesanver Writing Tests A test only fails if an exception is thrown.

Slide 70

Slide 70 text

@michellesanver Writing Tests Write simple tests using PHPUnits assertions.

Slide 71

Slide 71 text

@michellesanver Writing Tests But essentially test ANYTHING YOU’D LIKE, just throw exceptions.

Slide 72

Slide 72 text

@michellesanver Mink

Slide 73

Slide 73 text

@michellesanver Mink Simulate browser behaviour with Mink

Slide 74

Slide 74 text

@michellesanver Mink Clicking links!

Slide 75

Slide 75 text

@michellesanver Mink // wait for animation
 $session->wait(500); 
 // get loginlink
 $loginLinkSelector = '.hidden-phone a.login';
 $loginLink = $page->find('css', $loginLinkSelector); 
 // click loginlink
 $loginLink->click();

Slide 76

Slide 76 text

@michellesanver Mink Drag and drop

Slide 77

Slide 77 text

@michellesanver Fixtures and Mocking

Slide 78

Slide 78 text

@michellesanver Fixtures and Mocking

Slide 79

Slide 79 text

@michellesanver Fixtures and Mocking Any time API responses changed… We cried. :’(

Slide 80

Slide 80 text

@michellesanver Fixtures and Mocking PHP VCR Record HTTP interactions!

Slide 81

Slide 81 text

@michellesanver PhantomJS Testing JavaScript

Slide 82

Slide 82 text

@michellesanver PhantomJS Headless browser

Slide 83

Slide 83 text

@michellesanver Selenium Driver PhantomJS is a ghost!

Slide 84

Slide 84 text

@michellesanver PhantomJS @javascript
 Scenario: Searching for "tomaten"
 Given I am on "/sortiment/groceries"
 When I search for "tomatoes"
 Then I should see “Your search for tomatoes yielded"


Slide 85

Slide 85 text

@michellesanver PhantomJS /**
 * @When I search for :searchterm
 */
 public function iSearchFor($searchterm)
 {
 
 }

Slide 86

Slide 86 text

@michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);


Slide 87

Slide 87 text

@michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);


Slide 88

Slide 88 text

@michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);


Slide 89

Slide 89 text

@michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);


Slide 90

Slide 90 text

@michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);


Slide 91

Slide 91 text

@michellesanver Screenshot comparison Compare image code

Slide 92

Slide 92 text

@michellesanver Wrapup

Slide 93

Slide 93 text

@michellesanver Behat and Gherkin A BDD Framework with clearly defined behaviours

Slide 94

Slide 94 text

@michellesanver Contexts and Steps Steps are your tests living in a context, which contains your logic for testing

Slide 95

Slide 95 text

@michellesanver Testing JavaScript Use Mink with a driver, for instance PhantomJS to test dynamic parts of your website.

Slide 96

Slide 96 text

@michellesanver Still not mature for CSS and image comparison.

Slide 97

Slide 97 text

@michellesanver Behat is Open Source

Slide 98

Slide 98 text

@michellesanver Got a bit excited about one Testing Framework for ALL THE THINGS? As developers in an open source community the decision is ours.

Slide 99

Slide 99 text

@michellesanver Contribute to my talk!

Slide 100

Slide 100 text

@michellesanver Thank you!