Quick tip: Behat Bookmarks

‘Bookmarking’ pages in Behat scenarios lets us check user journeys involving URLs with IDs in them, without needing to know what those IDs are.


I recently encountered a Behat test scenario for searching a folder of documents online, which looks a little like this:

Scenario: Search a sub-folder for non-existent document
    Given I am on the admin document list page
      And I have clicked the "Title" of "Reports" in the admin document list
     When I search for "pterodactyl" in the admin
     Then the admin record list should be empty

The user journey involves clicking into a sub-folder to view its contents, then searching through those contents with a search form. I want to assert that after submitting the form, the user remains on the same page instead of being taken elsewhere.

Thanks to the Mink Extension MinkContext, we can already check URLs:

Then I should be on "/admin/documents"

The problem, however, is that our sub-folder URL contains a numeric ID: /admin/documents/{parentId}, and because we use data fixtures for all test content, we don’t know what that ID is (and don’t want to).

We could match the URL against a regular expression to check a parentId exists, but without knowing the ID it wouldn’t be accurate enough.

I want a way to ‘bookmark’ the current page, regardless of its URL, then check that I’ve returned to that exact page in the future, like this:

When I bookmark this page as "Current page"
 And I take some action...
Then I should be back on the page I bookmarked as "Current page"

By storing a small array of bookmark names and URLs, this can be accommodated in our FeatureContext:

<?php

use Behat\MinkExtension\Context\MinkContext;
use Behat\Behat\Context\Step\Then;

class FeatureContext extends MinkContext
{
    /**
     * @var array
     */
    protected $bookmarks = array();

    /**
     * @Given /^I (?:have bookmarked|bookmark) (?:this|the current) page as "(?P<bookmark>[^"]+)"$/
     */
    public function bookmarkThisPage($bookmark)
    {
        $this->bookmarks[$bookmark] = $this->getSession()->getCurrentUrl();
    }

    /**
     * @Then /^I should be(?: back)? on the page I bookmarked as "(?P<bookmark>[^"]+)"$/
     */
    public function shouldBeOnBookmark($bookmark)
    {
        if (!array_key_exists($bookmark, $this->bookmarks)) {
            throw new \Exception(sprintf('You haven’t yet created the bookmark: %s', $bookmark));
        }

        return new Then(sprintf('I should be on "%s"', $this->bookmarks[$bookmark]));
    }
}

So our final scenario ends up like this—not an ID in sight:

Scenario: Search a sub-folder for non-existent document
    Given I am on the admin document list page
      And I have clicked the "Title" of "Reports" in the admin document list
      And I have bookmarked this page as "Reports contents"
     When I search for "pterodactyl" in the admin
     Then I should be on the page I bookmarked as "Reports contents"
      And the admin record list should be empty