Skip to main content

Using Mink for web testing

PreviousNext have been using Behat to test Drupal 7 sites for some time now. More recently, there has been a big push to introduce Behat tests in Drupal 8. So what is it all about?

In this post, I'll be taking a look at the Mink layer behind Behat and using it for web scraping and functional testing and what this means for the future of Drupal testing.

by cameron.zemek /

Introduction

Many of us are familiar with Behat but you may not be aware that most of the built in step-definitions are added by the Mink extension, which is a Behat Extension that wraps Mink. The core of Mink is the DriverInterface which allows a swappable back-end for functional testing.

For this post going to look at three different backends for Mink: Goutte, Zombie.js, and PhantomJS. Each has its pros and cons.

  • Goutte - is a classical pure-php headless browser using Guzzle and Symfony DomCrawler under the hood.
    • Pro - Pure php so has no dependencies besides php.
    • Pro - Very fast and low overhead.
    • Con - No Javascript support.
    • Con - Custom DOM implementation.
  • Zombie.js - is headless browser using Node.js.
  • PhantomJS is a headless WebKit based browser with native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.
    • Pro - WebKit based so has an actual browser implementation.
    • Con - Only single instance of CookieJar which can be issue if wish to simulate multiple login sessions simultaneously using single instance of PhantomJS. Though this may be coming soon.

Setup

To get started in using Mink with composer add the Mink dependency and the drivers that wish to use:

{
  "require": {
    "behat/mink": "*",
    "behat/mink-goutte-driver": "*",
    "behat/mink-selenium2-driver": "*",
    "behat/mink-zombie-driver": "*"
  }
}

Run composer install to setup the required php packages (see Getting Started for introduction to composer). Now PhantomJS and Zombie.js require additional installation before can use them. PhantomJS is straight forward just download and unzip the binary. Then on command line start it with:

phantomjs --webdriver=4444

For Zombie.js you will need node.js and npm. With node/npm setup can install zombie with (Note: currently need version 1.4.1 for the stable mink zombie driver):

npm install -g zombie@1.4.1

Unlock Mink power

Now we are ready to write php code to use Mink to control the headless browsers.

<?php
// Use the composer autoloader.
require_once 'vendor/autoload.php';

// Setup mink drivers.
$goutteDriver = new \Behat\Mink\Driver\GoutteDriver();
$phantomjsDriver = new \Behat\Mink\Driver\Selenium2Driver('phantomJS');
$zombiejsDriver = new \Behat\Mink\Driver\ZombieDriver(new \Behat\Mink\Driver\NodeJS\Server\ZombieServer());

// Setup mink sessions.
$goutteSession = new \Behat\Mink\Session($goutteDriver);
$phantomjsSession = new \Behat\Mink\Session($phantomjsDriver);
$zombiejsSession = new \Behat\Mink\Session($zombiejsDriver);

// Setup mink session manager.
$mink = new \Behat\Mink\Mink();

// Register sessions.
$mink->registerSession('goutte', $goutteSession);
$mink->registerSession('phantomjs', $phantomjsSession);
$mink->registerSession('zombiejs', $zombiejsSession);

// Set Goutte as the default session.
$mink->setDefaultSessionName('goutte');

// Visit mink website with goutte driver.
$mink->getSession('goutte')->visit('http://mink.behat.org');

// Visit mink website with phantomjs driver.
$mink->getSession('phantomjs')->visit('http://mink.behat.org');

// Visit mink website with zombiejs driver.
$mink->getSession('zombiejs')->visit('http://mink.behat.org');

// Get the default goutte session.
$session = $mink->getSession();

// Get the page document.
$page = $session->getPage();

// Output the installation instructions from the page.
echo $page->find('css', '#installation p')->getText(), PHP_EOL;

// Click the Behat link
$page->find('css', '#web-acceptance-testing')->clickLink('Behat');

// Output the current URL of page
echo $page->getCurrentUrl(), PHP_EOL;

// Stop browser sessions.
$mink->stopSessions();

With Mink can traverse and find elements with XPath and CSS selectors; or fill in forms. Additional with drivers that support Javascript (eg. PhantomJS) can also control the mouse and interact with hovers and popups etc. Now we can build functional test with Mink, using PHPUnit as the test runner:

<?php
class ExampleTest extends \PHPUnit_Framework_TestCase {
  /**
   * @var \Behat\Mink\Session
   */
  protected $session;

  public function setUp() {
    $driver = new \Behat\Mink\Driver\GoutteDriver();
    $this->session = new \Behat\Mink\Session($driver);
    $this->session->start();
  }

  public function tearDown() {
    $this->session->stop();
  }

  public function testExample() {
    $this->session->visit('http://mink.behat.org');
    $page = $this->session->getPage();

    $this->assertEquals(
      'Mink is a php 5.3 library that you’ll use inside your test suites or project. Before you begin, ensure that you have at least PHP 5.3.1 installed.',
      $page->find('css', '#installation p')->getText()
    );
  }
}

So what does this mean for Drupal?

Originally Drupal's WebTestBase evolved from SimpleTest but over the years it has morphed and transformed into a mutant beast of difficult to maintain custom code. Whilst we've been off on our island, the PHP community at large have been working together on solving the same problems. In comparison we've been adding additional band-aids and bolt-ons to SimpleTest.

The solution is Mink as it allows us to reuse the work of the PHP community. Lee Rowlands, Nick Schuch and myself have been working on rewriting WebTestBase to use Mink while retaining as much backwards-compatibility as possible so Drupal can start to take steps off its' island and work towards integrating with rest of the PHP community. What this means is:

  • Removing lots of custom-code for parsing/manipulating the DOM and use widely adopted implementions.
  • Remove custom Curl handling and let the driver handle the requests (ie. Goutte/Guzzle).
  • Any issues we find strengthens those code-bases and contributes to the PHP community at large. We have already ported Goutte to Guzzle 4 (ie. Goutte 2.0) and found and fixed bugs in Guzzle 4.
  • Drupal testing framework will become less of a snowflake and be easier to maitain and work with.
  • We can move towards real Javascript testing by using Mink drivers with javascript support (eg. selenium2 with phantomJS).

If you interested to know more about what this means for Drupal come along to the core conversation at Amsterdam DrupalCon.

Senior Drupal Developer