Behat + Selenium2 / Webdriver with MinkExtension
If you just want the code, you can find it over on GitHub
Around a week ago, I wrote up my experience using Behat, Mink and Selenium2/WebDriver. As it turns out, whilst it was a good learning experience and it did work, I was going about it completely the wrong way.
Whilst working with Mink, I kept seeing mentions to MinkExtension, but didn't see any details anywhere so I just ignored it. Soon after publishing my last post, Everzet commented and posted a link to some MinkExtension docs. I've run through the install process and it's much easier. However, the docs aren't complete for using Selenium2 so I'm still going to document the process here.
Installing with Composer
As before, the easiest way to get Behat installed is to use Composer. Once you've installed Composer, create a composer.json
file that contains the following:
json
{"require": {"behat/behat": ">=2.2.2","behat/mink": "1.4@stable","behat/mink-selenium2-driver": "*","behat/mink-extension": "*"}}
Initialising Behat
Once that completes, you should be able to run behat with vendor/behat/behat/bin/behat
. That gets old pretty quickly though, so I like to run ln -s vendor/behat/behat/bin/behat behat
to create an alias in the current directory. If you do the same, you'll be able to run behat just by typing ./behat
You should be ready to run ./behat --init
and get your project started. This will create the features
folder, along with FeatureContext.php
. You'll also need a behat.yml
file. Create this file in the same directory as composer.json
with the following content:
yaml
default:extensions:Behat\MinkExtension\Extension:base_url: "http://YOUR-DOMAIN.com"default_session: selenium2browser_name: "firefox"selenium2:capabilities: { "browser": "firefox", "version": "14" }
The important bits here that aren't documented in MinkExtension are that your default_session should be selenium2, and that instead of using selenium2: ~
, you need to use it as a section and pass in your capabilities. Without this, MinkExtension will try and use Firefox version 8 and fail.
Creating a subcontext
Like before, I prefer to set up subcontexts to keep things clean, so create a new file in features/bootstrap
named GuiContext.php
with the following content:
php
use Behat\Behat\Context\BehatContext;use Behat\Gherkin\Node\PyStringNode;use Behat\Mink\Mink,Behat\Mink\Session,Behat\Mink\Driver\Selenium2Driver,Behat\MinkExtension\Context\MinkContext;use Selenium\Client as SeleniumClient;require_once 'PHPUnit/Autoload.php';require_once 'PHPUnit/Framework/Assert/Functions.php';class GuiContext extends MinkContext{}
By extending MinkContext, we inherit a lot of built in steps to use when driving Selenium. First though, we need to edit features/bootstrap/FeatureContext.php
to register our new subcontext. Add the following code inside the __construct
method:
php
$this->useContext('gui',new GuiContext($parameters));
You'll be able to run ./behat -di
now to view a list of all of the available steps, all provided by MinkExtension.
Selenium Server
Now, I'm not sure how this would work for people that work on GUI machines, but I develop in a virtual machine so there's no way to run browsers on them. To get around this, I use Selenium Grid to run the tests remotely on my iMac.
Firstly, you'll want to download Selenium Server on all the machines involved.
You'll want to run the following command on the machine that's running the tests (the machine on which you run ./behat
). This sets up a hub that can receive connections from a node reporting that there are browsers available to test with.
bash
java -jar selenium-server-standalone-2.25.0.jar -role hub
On the machine that you want to run the browser on, run the command:
bash
java -jar selenium-server-standalone-2.25.0.jar -role node -hub http://192.168.56.101:4444/grid/register -browser browserName=firefox,version=14,maxInstances=1
This starts up Selenium server in node mode. Node mode registers that you have available browsers and fires up browser windows to automate.
The IP address provided should be the IP address of the machine that you set up as a hub. The browser parameter is what browser will be used to run the test. Finally, I set maxInstances to 1 so that we only run one test at a time and my machine doesn't fall over.
Your first feature
Just like last time, let's build a simple feature to show how Mink + Behat work together. This time though, I'm going to use only the built in steps. I won't write any myself. So, create features/google.feature
and give it the following content:
Feature: Visit Google and search Scenario: Run a search for Behat
You might remember that one of the steps listed when we ran ./behat -di
was Given /^(?:|I )am on "(?P<page>[^"]+)"$/
. We're going to use this to visit google.com. Your google.feature
file should look like this:
Feature: Visit Google and search Scenario: Run a search for Behat Given I am on "http://google.com/?complete=0"
The next thing to do is to fill in the search box. We're going to use the step When /^(?:|I )fill in "(?P<field>(?:[^"]|\\")*)" with "(?P<value>(?:[^"]|\\")*)"$/
to achieve this. Then, we're going to use the step When /^(?:|I )press "(?P<button>(?:[^"]|\\")*)"$/
to click on the search button. Once you add those, your feature file should look something like the following:
Feature: Visit Google and search Scenario: Run a search for Behat Given I am on "http://google.com/?complete=0" When I fill in "lst-ib" with "Behat" And I press "Google Search"
Finally, we want to ensure that the first result is "Behat — BDD for PHP". To do this, we use the step ^(?:|I )should see "(?P<text>(?:[^"]|\\")*)" in the "(?P<element>[^"]*)" element$/
.
Feature: Visit Google and search Scenario: Run a search for Behat Given I am on "http://google.com/?complete=0" When I fill in "lst-ib" with "Behat" And I press "Google Search" Then I should see "Behat — BDD for PHP" in the ".vsc:first-child a" element
This looks as though it should work, but the final query runs before the search has time to execute, so we're going to have to introduce a delay. There was no method listed when we ran ./behat -di
to pause for a second or two, so we'll add one to GuiContext.php
(I know I said we wouldn't add any, but this is barely a method).
So, if you add the following step to GuiContext.php:
php
/*** @When /^wait (\d+) seconds?$/*/public function waitSeconds($seconds){$this->getSession()->wait(1000*$seconds);}
Our feature file becomes:
Feature: Visit Google and search Scenario: Run a search for Behat Given I am on "http://google.com/?complete=0" When I fill in "lst-ib" with "Behat" And I press "Google Search" And wait 1 second Then I should see "Behat — BDD for PHP" in the ".vsc:first-child a" element
If we run ./behat
all the steps should go green. Congratulations, you have a passing test that uses (almost) exclusively built in MinkExtension steps. This is much easier than integrating Mink yourself, and it even fixes the issue with the browser window appearing that my previous solution had.