Introduction

Welcome to another episode of Magecasts.io. Last time we covered how to install and configure Behat and Selenium as well as running a very simple example where we searched wikipedia.

During this lesson I will cover the basics of the domain specific language Behat uses called Gherkin.

Dan north started the BDD movement and calls BDD a communication and collaboration framework for developers. He proposed that we should write the scenarios using Given, When, Then. Note these scenarios are not written by a single person. They are a collaboration between stake holders product owners developers QA etc. And they represent the behaviour of a specific business process. However we are looking at the features files so if you imagine a full collaboration process has happened on this before hand were all cool.

Types of scenarios

Imperative Style

Imperative scenarios use highly reusable granular steps which outline much of the user interface. Lets take a look at how we might write a scenario for adding an item to the cart and see how this might look:

1 Scenario: Add a product to the basket and top cart increases
2    Given I am on "product1.phtml"
3    When I click ".itemSizeSmall"
4    And I click ".male"
5   And I click ".addToCart"
6   Then I should see ".headerCart" has "1" item in it

Now none of us write scenarios like this do we ? Well it wasn't long ago that I did and I saw nothing wrong with it.. Its testing the feature I'm working on. Its running in CI ( sort of ) and everyone inspects the element on a site before adding it to the cart don't they ?

So still in the imperative style lets try and improve this scenario. The first thing we will do is remove the implementation details. CSS selectors change and they make no sense when we talk to non technical people.

1 Scenario: Add a product to the basket and top cart increases
2    Given I am on "product1.phtml"
3    When I click small clothing size
4    And I click male gender
5   And I click add to cart
6   Then I should see the top cart has "1" item in it

Cool so its less coupled to the interface now but just testing that the top cart updates when we add a item involves a lot of steps that we are describing in our scenarios. I can see as this project increases we copy paste these lines all over the place and when we introduce a step or remove a step etc then thats a lot of places to update. Plus do we ever talk about clicking things when were in a meeting with the client.. What value does it offer talking like this ?

Declarative style

This style is more aligned to user stories in the "Agile sense" where we describe the actual behaviour in a conversational style. Lets take our adding item to cart example and see how that might look:

1 Scenario: Add a product to the basket
2    When I add the Small mens item to the basket
3    Then I should see that the top cart increases by "1"

How does that work in Behat ?

Lets see how we would code such an example in php using Behat. Lets run behat so that we can generate our feature context.

 1 <?php
 2 class BasketFeature extends RawMinkContext ...
 3 {
 4     public function whanIaddAsmallMens...()
 5     {
 6          $sizeOption =  $this->getSession()->getPage()->find('css', '.sizeItem');
 7          $sizeOption->selectOption('small');
 8 
 9          $gender = $this->getSession()->getPage()->find('css', '.gender');
10          $gender->selectOption('male');
11 
12          $this->getSession()->getPage()->find('css', '.addToCart')->click();
13     }
14 
15      public function checkTotalIsUpdate()
16      {
17            $total = $this->getSession()->getPage()->find('css', '.cartTotals')->getText();
18 
19            expect($total)->toBe($some other total);
20 
21             PHPUnit::assertEquals($total, $some other total);
22      }
23 }

As you can see for the assertions we can use any check we want. I prefer expect at times as it makes sense when I read it outline, However PHPUnit assertion lib is more powerful and an exception tool to use.

Previously I showed the raw mink context. However you might have noticed that there is a MinkContext that can be used. This context is great for people who are getting started with Behat but it has many issues from my perspective. Lets take a look at both its strengths and its fallings at the same time:

https://github.com/Behat/MinkExtension/blob/master/src/Behat/MinkExtension/Context/MinkContext.php

How cool, Look at all of the step definitions that are provided for us.

  • I visit.
  • I do this
  • I do that.
  • I click link
  • I see.

This will save so much time. And correct it can save time to use the MinkContext if used correctly. The problem that I faced when using this context came from changing the scenarios to match the context file. It became to coupled to how I implemented scenarios in behat.

Sometimes we want to debug out running scenario. Now we can resort to the old method die(); vardump, printr etc. However one amazing extension I have found recently is my Ciaran McNuttley and it allows us to step through a running scenario. Lets use our example for the last workshop to debug the wikipedia steps.

** Disclaimer if you haven't watched the first video its a good idea to head over there first to get up to speed ** Lets see how we can debug our scenario then. Lets start by adding the extension to composer.json

1 {
2    "require-dev": {
3         "ciaranmcnulty/behat-stepthroughextension" : "dev-master"
4     }
5 }

then we need to update our vendors directory composer update this as you know will pull down all of the dependencies required for this extension.

Once all of this is installed we need to updated Behat to let it know that there is another extension. Open up behat.yml file and add the extension. Note that the tild indicates that there are no options for configuring this extension.

1 default:
  extensions:
    Cjm\Behat\StepThroughExtension: ~
   ...

Now if we run behat from the command line lets see if we have any new options: ./bin/behat you should see that there is a new option now for step-through. Lets run our search feature with the new option --step-through and lets see how this changes the flow.

How to be selective on running only certain features ?

This is looking good however all we have been doing so far is running the entire suite. Now thats OK for us as we only have a single scenario. Yet what happens when the test suite grows and grows ? Well we could take this time for a coffee break ? Im sure we can get that past the managers for a few weeks. Or we can look at some of the ways that we can run only certain parts of the feature files. Lets start by adding another scenario so we can show the difference.

1 Scenario: test scenario

2 Given I am doing something

3 When I do something else

4 Then I should see the last thing I did

When we run behat this time we are going to see that our complete scenario runs as passes like we expect but also we get the undefined step definitions. Not really ideal having both of them running when we want to focus on the new one. So how can we only run the WIP scenario? Well first off we can run only the one scenario based on the line number:

./bin/behat features/search.feature:7

As you can see this time its only running the scenario that starts on line 7. Pretty cool.

But lets take this a step further. In our feature file we might want to group the scenarios. We might mark some as WIP and some as Complete. Normally we would use tags to represent behaviour. Or even what session manager to use JS for example. However lets add @WIP to the incomplete scenario and @Complete to the complete one and see how we can run each on there own.

 1 @Complete
Scenario
 2 Search Wikipedia for BDD

 3 Given I am on wikipedia

 4 When I search for "Behaviour driven development"

 5 Then the first heading will be "Behavior-driven development"


 6 
 7 @WIP
Scenario: test scenario

 8 Given I am doing something

 9 When I do something else

10 Then I should see the last thing I did

Now we can run behat and specify what "tag" we want to execute:

./bin/behat --tags WIP OR ./bin/behat --tags Complete

As you can see here only the scenarios that match that annotation are executed. The benefit here is that multiple scenarios can match those tags and they will all be run. This applied to all features files unless you specify a specific feature file in the path.

So far we have seen how we can run all features. How we can run individual features and how we can group by using tags. Now lets take a look at suites. What is a suite ? Well a suite represents a group of concrete features with all the information on how to test them. Recently I have been using suites to test the core API and another for the web interface.

Lets take a look at how we can configure suites. We start by editing our Behat.yml file

1 suites:
2           web_features:
3               paths:    [ %paths.base%/features/web ]
4               contexts: [ WebContext ]
5           core_features:
6               paths:    [ %paths.base%/features/core ]
7               contexts: [ CoreContext ]

Lets use behat to initialise the suites for us. ./bin/behat —init we should see that behat has created for us some context files and folders where we can place our feature files.

+d features/web - place your *.feature files here
+f features/bootstrap/WebContext.php - place your definitions, transformations and hooks here
+d features/core - place your *.feature files here
+f features/bootstrap/CoreContext.php - place your definitions, transformations and hooks here

So here I will be placing all of the web interface related contexts in the Web folder, and when I want to test the low level core API code then I will place them into the core folder.

Running the suites is as simple as ./bin/behat —suite=web_features || core_features

We haven't really started to build anything real yet but Id hope by now you understand:

  • How to install Behat, Selenium etc.
  • How to run Behat
  • The different types of story writing techniques
  • Knowledge of MinkContext and a warning of its powers.

Next up we will go through creating a simple application so that we can see all that we learned in this lesson put to action.


comments powered by Disqus
Comment Permalink