[How2Tips] How to wait for AJAX responses in Behat with Mink?

Published on

Nov 23, 2017

how2tips

We all know this situation where we need to test a UI feature that contains asynchronous calls. We also know that Behat is maybe not the best tool for that, but hey. We don't always use a JS test framework.

The Selenium wait method

The traditional way is to directly evaluate a javascript statement thanks to Selenium:

if (false === $this->getDriver() instanceof \\Behat\\Mink\\Driver\\Selenium2Driver) {
    throw new \\Exception('This step must be run with a JS driver');
}

if (false === $this->getDriver()->wait(5000, "$('body').is(':not(.ajax)')")) {
    throw new \\RuntimeException('Ajax request in progress. Modal content not loaded.');
}

The Spin pattern

Actually we can use pure PHP to wait for something to happen and don't care anymore about javascript, thanks to a custom spin method. This is documented here: Here is how it works. A spin method is implemented in the Context:

private function spin($lambda, $wait = 10)
{
    for ($i = 0; $i < $wait; $i++) {
        try {
            if ($lambda($this)) {
                return true;
            }
        } catch (\\Exception $e) {
        }

        sleep(1);
    }

    $backtrace = debug\_backtrace();

    throw new \\Exception(
        "Timeout thrown by " . $backtrace\[1\]\['class'\] . "::" . $backtrace\[1\]\['function'\] . "()\\n" .
        $backtrace\[1\]\['file'\] . ", line " . $backtrace\[1\]\['line'\]
    );
}

Then this method can be used by passing a closure that will return true if the element is visible, or throw an exception if not:

return $this->spin(function() {
    if (null === $this->find('css', '.some-element')) {
        throw new \\Exception();
    }

    return true;
}, 10);

This will try every second to find the element. After the 10 seconds we configured, if nothing was found, it finally throws an exception.

The Mink direct implementation

Actually, since the version 1.6.0, this is directly implemented in Mink, thanks to the waitFor method. It works a bit differently. It can be used this way:

return $this->waitFor(10000000, function() {
    return $this->find('css', '.some-element');
});

As you can see a few differences can be spotted:

  • The callback is here the second argument
  • The timeout is given in microseconds (in this example, 10 000 000 means 10 seconds)
  • If the result is still not what you expect, you must not throw an exception (otherwise it will stop spinning) but return a non truthy value.

You can now work with Mink without directly dealing with Javascript and Selenium.

This article is from our internal knowledge base, we wanted to share it with you. ❤  Send us a KUDO-Tweet if this article has helped you !

Written by

Yann Rabiller
Yann Rabiller

Comments