Behat Extension

Run Gherkin scenarios in a real browser: extension wiring, configuration, and custom step contexts.

The playwright-php/playwright-behat package is a Behat extension that runs scenarios in a Playwright-controlled browser. Feature files stay in Gherkin; the steps drive Chromium, Firefox, or WebKit.

bash
composer require --dev playwright-php/playwright-behat
vendor/bin/playwright-install --browsers

Wiring

Enable the extension by its class name, then add a context to the suite that should use a browser.

yaml
default:
  extensions:
    Playwright\Behat\ServiceContainer\PlaywrightExtension:
      base_url: 'http://localhost:8000'
  suites:
    web:
      paths: ['%paths.base%/features']
      contexts:
        - Playwright\Behat\Context\PlaywrightContext

Behat 4 removed YAML configuration. The PHP form below is read by Behat 3 and 4 alike, so it is the safer file to write today:

php
<?php
// behat.php

use Behat\Config\Config;
use Behat\Config\Extension;
use Behat\Config\Profile;
use Behat\Config\Suite;
use Playwright\Behat\Context\PlaywrightContext;
use Playwright\Behat\ServiceContainer\PlaywrightExtension;

return (new Config())
    ->withProfile((new Profile('default'))
        ->withExtension(new Extension(PlaywrightExtension::class, [
            'base_url' => 'http://localhost:8000',
        ]))
        ->withSuite((new Suite('web'))
            ->withPaths('%paths.base%/features')
            ->withContexts(PlaywrightContext::class)));

One browser, one page per scenario

The extension launches a single browser for the whole run and opens a fresh BrowserContext and page for each scenario. Scenario isolation comes from the context, not from a relaunch, so a suite pays the browser startup cost once.

Nothing in a feature file starts or stops the browser. The page opens the first time a step asks for it and closes when the scenario ends.

Configuration

Option Default Effect
browser chromium Engine to launch: chromium, firefox, or webkit
headless true Run without a visible window
base_url null Prefix for relative URLs in navigation steps
timeout 30000 Default timeout for actions and navigations, in milliseconds
slow_mo 0 Delay between browser operations, in milliseconds
viewport 1280x720 Viewport of every scenario page
screenshot_dir %paths.base%/var/screenshots Where screenshots are written
auto_screenshot_on_failure true Write failed-<scenario>-<line>.png when a scenario fails

Built-in steps

Five patterns ship with PlaywrightContext. Selectors are Playwright selectors, so CSS works alongside text= and role=.

gherkin
Given I am on "/login"
When I go to "/dashboard"
When I click on "button[type=submit]"
When I fill "#email" with "user@example.com"
Then I should see "Welcome back"
When I take a screenshot named "after login"

Then I should see matches the page HTML, so text broken across markup does not match. When a step fails, the exception is Playwright\Behat\Exception\ExpectationFailedException, or a Playwright PHP timeout when the element never became actionable.

Custom steps

Anything beyond those five patterns belongs in a project context. Extend RawPlaywrightContext and use the Page directly:

php
use Behat\Step\When;
use Playwright\Behat\Context\RawPlaywrightContext;

final class AdminContext extends RawPlaywrightContext
{
    #[When('I sign in as an administrator')]
    public function signInAsAdministrator(): void
    {
        $page = $this->getPage();
        $page->goto('/admin/login');
        $page->locator('#username')->fill('admin');
        $page->locator('#password')->fill('secret');
        $page->locator('[type="submit"]')->click();
    }
}

getPage() returns the page of the current scenario, opening it on first call. A context that already extends another class can implement PlaywrightAwareContext instead and receive the shared PlaywrightManager.

In CI

Install browsers before running Behat, then upload the failure screenshots as artifacts. The GitHub Actions setup page covers the install step; point the artifact path at the screenshot_dir you configured.

Choosing this over Mink

Both run Behat suites on Playwright browsers. The difference is what your suite already depends on.

You have Use
Hundreds of Mink steps and page objects Mink driver, which keeps the Session API
Feature files but no Mink, or a new suite This extension, which exposes Playwright directly
Browser tests owned by developers, no Gherkin readers PHPUnit with Playwright PHP, no bridge at all

Go next