You can use Behat to describe anything, that you can describe in business logic. It’s tools, gui applications, web applications. Most interesting part is web applications. First, behavioral testing already exists in web world - it’s called functional or acceptance testing. Almost all popular frameworks and languages provide functional testing tools. Today we’ll talk about how to use Behat for functional testing of web applications.
One of the most important parts in the web is a browser. Browser is the window, through which web application users interact with application and other users. User is always talking with web application through browser application.
So, in order to test web application, we should transform user actions into steps and expectations - with Behat it’s quite simple already. And next part is much harder - do this actions and test expectations. How to programmatically do things like this:
Given I am on "/index.php"
You’ll need something to simulate browser application. Scenario steps would simulate user and browser emulator would simulate browser with wich user interacts in order to talk with web application.
Now the real problem. We have 2 completely different type of solutions:
The problem is we need both these emulator type in order to do successful functional testing. Because both these tools are quite limited at some parts, but succeed on others. For example, you can’t use in-browser emulators for all tests in your application, cuz this makes your tests become very slow. Also, you can’t do AJAX with headless browser.
You should use them both. But there comes a problem - these are very different tools and they have much different API. Use both those API limits us very much and in case of Behat, this problem become even worse, cuz now you have single:
When I go to "/news.php"
And this step should be somehow executed through one or another browser emulator.
Here comes Mink. Mink is a browser emulators abstraction layer. It hides emulators differences behind single, consistent API.
Just some of the benefits:
Mink is a php 5.3 library that you’ll use inside your test and feature suites. Before you begin, ensure that you have at least PHP 5.3.1 installed.
The simplest way to install Behat with Mink is through Composer.
Create composer.json file in the project root:
{
"require": {
"behat/behat": ">=2.2.2",
"behat/mink": ">=1.3.2"
},
"repositories": [
{
"type": "composer",
"url": "behat.org"
}
],
"config": {
"bin-dir": "bin/"
}
}
Then download composer.phar and run install command:
$ wget -nc http://getcomposer.org/composer.phar
$ php composer.phar install
After that, you will be able to run Behat with:
$ bin/behat
And this executable will already autoload all the needed classes in order to work with Mink through Behat.
You can also install Mink through PEAR:
$ pear channel-discover pear.behat.org
$ pear install behat/mink-beta
Now, you can use Mink in your projects simply by including it:
require_once 'mink/autoload.php';
Also, you can use mink phar package:
$ wget https://github.com/downloads/Behat/Mink/mink.phar
Now you can require phar package in your project:
require_once 'mink.phar';
Mink comes with ready to work Behat FeatureContext implementation. It’s called MinkContext and it uses PHPUnit assertions internally, so you need to install latest PHPUnit in order to use bundled with Mink steps:
$ pear channel-discover pear.phpunit.de
$ pear channel-discover components.ez.no
$ pear channel-discover pear.symfony-project.com
$ pear install phpunit/PHPUnit
Mink comes with Behat\Mink\Behat\Context\MinkContext context class. You could either inherit or subcontext it (see FeatureContext chapter):
# features/bootstrap/FeatureContext.php
<?php
use Behat\Behat\Context\ClosuredContextInterface,
Behat\Behat\Context\BehatContext,
Behat\Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode,
Behat\Gherkin\Node\TableNode;
// to use mink installed with PEAR:
// require_once 'mink/autoload.php';
// or, if you want to use phar from current dir:
// require_once __DIR__ . '/mink.phar';
// nothing required here if you installed mink with Composer!
class FeatureContext extends Behat\Mink\Behat\Context\MinkContext
{
}
Notice, that we’ve extended Behat\Mink\Behat\Context\MinkContext context class. This context comes bundled with bunch of very useful web steps. To see all available steps and to check, that Mink is included correctly - call:
$ behat -dl
If all works properly, you should see something like this:
Now, all navigation steps in Mink uses relative paths. In order to be able to make full URL’s from relative paths, Mink should know about base_url value. And you can help Mink with it:
# behat.yml
default:
context:
parameters:
base_url: http://en.wikipedia.org/
Tip
base_url should always end up with /.
Let’s for example write a feature to test Wikipedia search abilities:
# features/search.feature
Feature: Search
In order to see a word definition
As a website user
I need to be able to search for a word
Scenario: Searching for a page that does exist
Given I am on "/wiki/Main_Page"
When I fill in "search" with "Behavior Driven Development"
And I press "searchButton"
Then I should see "agile software development"
Scenario: Searching for a page that does NOT exist
Given I am on "/wiki/Main_Page"
When I fill in "search" with "Glory Driven Development"
And I press "searchButton"
Then I should see "Search results"
We have two scenarios here:
Now, run your feature:
$ behat features/search.feature
You’ll see output like this:
Ok. We’ve successfully tested wikipedia search and it works flawlessly. But what about search field autocompletion? It’s done using JS and AJAX, so we can’t use default headless session to test it - we need javascript session and Sahi browser emulator for that task.
Sahi gives you ability to take full controll of real browser with clean consistent proxy API. And Mink uses this API extensively in order to use same Mink API and steps to do real actions in real browser.
All you need to do is install Sahi:
Download and run the Sahi jar from the http://sahi.co.in/w/
Run sahi proxy before your test suites (you can start this proxy during system startup):
cd $YOUR_PATH_TO_SAHI/bin
./sahi.sh
That’s it. Now you should create specific scenario in order it to be runnable through Sahi:
Scenario: Searching for a page with autocompletion
Given I am on "/wiki/Main_Page"
When I fill in "search" with "Behavior Driv"
And I wait for the suggestion box to appear
Then I should see "Behavior Driven Development"
Now, we need to tell Behat and Mink to run this scenario in different session (with different browser emulator). Mink comes with special hook, that searches @javascript or @mink:sahi tag before scenario and switches current Mink session to Sahi (in both cases). So, let’s simply add this tag to our scenario:
@javascript
Scenario: Searching for a page with autocompletion
Given I am on "/wiki/Main_Page"
When I fill in "search" with "Behavior Driv"
And I wait for the suggestion box to appear
Then I should see "Behavior Driven Development"
Now run your feature again:
$ behat features/search.feature
And of course, you’ll get:
That’s because you haven’t defined the Then I wait for the suggestion box to appear step yet. But don’t worry, Behat already suggested the regex and function snippets that you can use and Mink makes writing new steps easy:
/**
* @Then /^I wait for the suggestion box to appear$/
*/
public function iWaitForTheSuggestionBoxToAppear()
{
$this->getSession()->wait(5000,
"$('.suggestions-results').children().length > 0"
);
}
That simple. We get current session and send JS command to wait (sleep) for 5 seconds or until expression in second argument returns true. Second argument is simple jQuery instruction. Run feature again and:
Voila!
Tip
Context isolation is very important thing in functional tests. But restarting the browser after each scenario could slow your feature suite very much. So, by default Mink tries hard to reset your browser session without reloading it (cleans all domain cookies).
In some cases it might be not enough (when you use http-only cookies for example). In that case, just add @insulated tag to your scenario. Browser in this case will be fully reloaded and cleaned (before scenario):
Feature: Some feature with insulated scenario
@javascript @insulated
Scenario: isolated scenario
...
Read more cookbook articles on Behat and Mink interactions: