Functional testing involves testing an application or website from the perspective of a user, through the user interface, ensuring that the core functionality works as expected. Automating this process is a vital QA technique, giving a heavily reproducible set of tests to detect bugs being introduced into an application. However, this is often overlooked by developers due to the overhead of writing these automated tests initially, despite the amount of problems and time it can save further down the road.

Mink is a PHP package providing a simple API for a multitude of browser automation tools, it can be used to automate browsers for many purposes. By combining Mink with a testing framework such as PHPUnit, and a browser driver such as Sahi or Selenium, it can be used as a very powerful functional testing tool.

All the code used in the following examples is available at https://github.com/gaw508/tutorials/tree/master/mink-functional-testing-tutorial

Setting Up

We will use composer in order to manage the dependencies of our tests. Below is the contents of our composer.json file.

{
   "require": {
       "phpunit/phpunit": "^4",
       "behat/mink": "^1.7",
       "behat/mink-sahi-driver": "^1.2"
   }
}

You’ll notice that we are have three dependencies:

  • PHPUnit: This is our testing framework, which will be used to make our assertions. If you are not familiar with PHPUnit, take a look at https://phpunit.de/

  • Mink: This is our browser emulation API, Mink controls our browser automation tools.

  • Mink Sahi Driver: This is the Mink driver for the Sahi browser automation tool. Sahi is one of a number of popular automation tools, another popular one is Selenium.

Run composer install to get the dependencies.

As mentioned above, we will be using Sahi as our automation tool, Sahi needs to be installed to your machine separately to our composer dependencies. The download can be found at http://sahipro.com/sahi-open-source/. Once installed, you can run sahi using the command line or the GUI tool it installs. Sahi needs to be running before running our tests. Mink makes it easy for us to change our browser controller, and we can easily switch to a similar one such as Selenium or a headless emulator such as Goutte.

Now we have our project set up with dependencies and Sahi installed, we can start writing our tests. First, though, we will write a PHPUnit test case template class for our Mink tests which we can extend.

tests/MinkTestCase.php

<?php

use Behat\Mink\Driver\SahiDriver;
use Behat\Mink\Session;
use Behat\Mink\Driver\DriverInterface;

class MinkTestCase extends PHPUnit_Framework_TestCase
{
    /**
     * @var DriverInterface  the Mink driver object
     */
    protected static $driver;

    /**
     * @var Session  the Mink session
     */
    protected static $session;

    /**
     * Function run before the first test in the class
     * Set up the Sahi driver and start a session, which will open the browser
     */
    public static function setUpBeforeClass()
    {
        static::$driver = new SahiDriver('chrome');
        static::$session = new Session(static::$driver);
        static::$session->start();
    }

    /**
     * Function run after each test in the class
     * Resets the session, clearing cookies etc.
     */
    protected function tearDown()
    {
        static::$session->reset();
    }

    /**
     * Function run after the last test in the class
     * Stops the session, and the browser is closed.
     */
    public static function tearDownAfterClass()
    {
        static::$session->stop();
    }
}

Here you can see we have 3 methods defined, including setUpBeforeClass (run before the first test in the class is run), tearDown (run directly after each test in the class) and tearDownAfterClass (run after the last test in the class).

  • The setUpBeforeClass method first sets up our Sahi driver with the Chrome browser, and then starts a Session, which opens up the browser window.

  • The tearDown method resets the session, effectively clearing cookies and browsing data without closing the browser.

  • The tearDownAfterClass method stops the sessions and closes the browser window.

All of these methods will be common to all of our tests, so this class will be extended by each.

In order to test that our setup works we will create a new test file called tests/HomePageTest.php

tests/HomePageTest.php

<?php

class HomePageTest extends MinkTestCase
{
    /**
     * Just a quick test to make sure that our Sahi installation is working before getting started with writing tests
     */
    public function testThatMinkWorks()
    {
        static::$session->visit('http://george.webb.uno/');
    }
}

The first thing to notice is that our class extends our MinkTestCase template, meaning we can just write a very simple method to open a web page without the need to do any setup. Calling the static::$session->visit(‘http://george.webb.uno/’) method opens that page in the Chrome window. Try running vendor/bin/phpunit (make sure Sahi is running first) in order to run through the test. Nothing is being asserted here, but you should see the Chrome window open, visit my website and then close again.

Planning Our Tests

For the purpose of this tutorial I have created a very basic page which we can test, this page can be found at http://mink-test-site.webb.uno/ and contains a header and a simple form which changes some words on the page. There a several things we can test on this page, and before we begin testing we need to define exactly what will be tested.

  • Test that the page header is “Mink Tutorial Test Site”
  • Test that the select box contains all 3 options ("happy", "angry", "sad").
  • Test that selecting “happy”, “angry” or “sad” changes the text accordingly.

These tests check that the core functionality of the site works as expected (the feelings select box) as well as checking the page header is correct. Next we will write the tests.

Writing the Tests

As all of these tests are on our homepage, we can put our tests into the HomePageTest.php file we created earlier.

tests/HomePageTest.php

<?php

class HomePageTest extends MinkTestCase
{
    /**
     * @var DocumentElement  the mink document element
     */
    protected $page;

    /**
     * This function runs before each test
     * Navigates the user to the home page
     */
    protected function setUp()
    {
        static::$session->visit('http://mink-test-site.webb.uno/');
        $this->page = static::$session->getPage();
    }

    /**
     * Test the page header is correct
     */
    public function testPageHeader()
    {
        $header = $this->page->find('css', 'h1');
        $this->assertEquals(
            "Mink Tutorial Test Site",
            $header->getText()
        );
    }

    /**
     * Test the functionality of the feelings select box
     */
    public function testFeelingsSelectBox()
    {
        // Get the select element and an array of the option elements within it
        $select = $this->page->find('css', '#feelings');
        $options = $select->findAll('css', 'option');
        // Get the span containing the feeling text
        $feeling_span = $this->page->find('css', '.i-feel');

        // Get the value of each option
        $option_values = array();
        foreach ($options as $option) {
            $option_values[] = $option->getValue();
        }

        // Assert that the options are happy, sad and angry
        $this->assertEquals(
            array(
                'happy',
                'sad',
                'angry'
            ),
            $option_values
        );

        // Check the initial text value is happy and the select is initially happy
        $this->assertEquals('happy', $select->getValue());
        $this->assertEquals('happy', $feeling_span->getText());

        // Change the value of the select to angry and check the text
        $select->selectOption('angry');
        $this->assertEquals('angry', $feeling_span->getText());
    }
}
  • We have added a setUp method here which before each test ensures the browser session is on the right page and then gets the document element from the page.

  • The first test method is the test for the page header. This first uses the find method on the page to retrieve the h1 tag and then the getText method on the h1 element to get the header text. This text is tested with a PHPUnit assertEquals.

  • The second test method tests the select box, and contains multiple assertions.

    • First we assert that the options within the select box are what we expect (happy, sad, angry).

    • Next we assert that the initial value of both the select box and the text span is happy.

    • Finally we assert that when we change the select box to angry, the text span changes accordingly.

Running the Tests

Now we have written our tests, we need to run them in order to get the results. Remember that before we can run the tests Sahi needs to be running.

  • Open your terminal window, and navigate to the directory where your project’s phpunit.xml file is.

  • Run vendor/bin/phpunit

  • You will start to see the browser open and run the sequence of tasks we defined within our tests. This will happen very quickly, and you will start to see results appearing in your terminal.

  • After the tests have finished you will see the PHPUnit test summary, which should be a green bar saying something like “OK (2 tests, 5 assertions)” this means that all of our tests passed!

  • Now that we have a test suite set up we can run them every time we make changes to make sure we haven’t broken any functionality.

In Conclusion

Once again, you can find all of the above code samples at https://github.com/gaw508/tutorials/tree/master/mink-functional-testing-tutorial

These are a very straightforward set of tests to give an insight as to what is possible with Mink. I would thoroughly recommend taking a further look into Mink’s documentation at http://mink.behat.org/en/latest/

As a next step you could try to automate the running of the test suite in multiple browsers and devices, to test that the site behaves the same in each. A great way to easily access all of these devices is by using BrowserStack automate (https://www.browserstack.com/automate). With this you can create remote connections to Selenium in BrowserStack directly from your running Mink code.