Installation
Follow this page to install Behat Steps in your PHP project, add the packages only some steps need and set up a real browser for scenarios that run JavaScript. To write your first scenario, follow the Quick start.
Before you begin
Check the following before you start:
- PHP 8.3 or newer, and Composer.
- Behat 3 from 3.33, Behat 4 or no Behat yet. Composer installs Behat with Behat Steps.
- Drupal 11 or 12, if you want the Drupal steps.
- No
drupal/drupal-extensionordrupal/drupal-driverin your project, required directly or pulled in by another package. Behat Steps does their job itself and declares a conflict with both, so Composer won't install it beside either one. The Requirements section of the migration guide shows the command that removes them. When another package pulls either one in, remove or update that package first. The rest of the migration guide lists what replaces their steps and classes.
Supported versions lists the full matrix.
Install the package
Run this in your project's root:
composer require --dev drevops/behat-steps:^4
That's the only package you need to require. Composer installs the rest with it, because Behat Steps requires them:
- Behat, as
behat/behat - Mink, the library Behat uses to drive a browser, as
behat/mink - Mink's BrowserKit driver, as
behat/mink-browserkit-driver - Mink's Behat extension, as
friends-of-behat/mink-extension - The Symfony components the steps use
Confirm that Composer installed the package: composer show drevops/behat-steps prints the installed release and the packages it requires.
A few steps need 1 more package each, which Optional dependencies lists.
Optional dependencies
Behat Steps lists 4 packages under suggest in its composer.json, because only some steps need them. composer suggests drevops/behat-steps lists the ones your project doesn't have yet. Add the ones your suite needs to your project's require-dev:
| Package | What needs it |
|---|---|
softcreatr/jsonpath | The JSON path steps of JSON responses, such as Then the JSON path :path should exist, and the REST APIs steps that set a header from a JSON path |
justinrainbow/json-schema | The JSON schema steps of JSON responses, such as Then the response should match the JSON schema in the file :filename |
lullabot/mink-selenium2-driver | @javascript scenarios run through a Selenium server |
dmore/behat-chrome-extension | @javascript scenarios run in headless Chrome, with no Selenium server |
The Selenium driver and the Chrome extension do the same job. For @javascript scenarios, install 1 of them, as JavaScript browser drivers describes. For a suite without @javascript scenarios, install neither. To use every JSON step, add both JSON packages:
composer require --dev softcreatr/jsonpath justinrainbow/json-schema
JavaScript browser drivers
Mink talks to the browser through a browser driver. The BrowserKit driver that comes with Behat Steps loads pages through HTTP requests from PHP. It's fast, but it never runs the page's JavaScript. Tag a scenario that needs JavaScript @javascript, and Mink runs it in a real browser through a second driver.
Behat Steps ships adapters, the classes that declare what a driver can do, for 2 real-browser drivers:
- The Selenium/WebDriver browser driver from
lullabot/mink-selenium2-driver dmore/chrome-mink-driver, which talks to Chrome directly over the Chrome DevTools Protocol, with no Selenium server
Both run JavaScript, and this library's own CI tests against both. What each one can do lists the capabilities only 1 of them provides. Browsers compares them with BrowserKit and lists the steps that need a real browser. Another browser driver needs an adapter registered for it.
Run JavaScript scenarios in headless Chrome
In these steps, you edit the behat.php file the Quick start creates. If your project has no behat.php yet, follow the Quick start first. To run @javascript scenarios in headless Chrome, with no Selenium server:
-
Add
dmore/behat-chrome-extension, which pulls indmore/chrome-mink-driver. This library's own CI runs it on both Behat majors:composer require --dev dmore/behat-chrome-extension -
In
behat.php, register the Chrome extension and add achromesession that points at a headless Chrome. It sits beside thebrowserkit_httpsession the Quick start sets up.$profileis thedefaultprofile of that file:use Behat\Config\Extension;use Behat\MinkExtension\ServiceContainer\MinkExtension;use DMore\ChromeExtension\Behat\ServiceContainer\ChromeExtension;$profile->withExtension(new Extension(ChromeExtension::class))->withExtension(new Extension(MinkExtension::class, ['base_url' => 'http://your-site.local','javascript_session' => 'chrome','sessions' => ['browserkit_http' => ['browserkit_http' => NULL],'chrome' => ['chrome' => ['api_url' => 'http://chrome:9222']],],]));This is the whole
MinkExtensionentry. A profile takes each extension once, and Behat stops with an error saying the extension already exists when a profile registers it twice. If your profile already registersMinkExtension, addjavascript_sessionand thechromesession to that entry and keep your ownbase_urland sessions.Mink runs every scenario tagged
@javascripton thejavascript_session, and every other scenario on the first session that can't run JavaScript,browserkit_httphere. Only the tagged scenarios run in Chrome.In
'chrome' => ['chrome' => [...]], the firstchromenames the session thatjavascript_sessionpoints at. The secondchromepicks the Chrome driver, whose settings follow.api_urlis the address of Chrome's DevTools endpoint. Inhttp://chrome:9222,chromeis the host name Behat reaches Chrome at, such as the name of its Docker Compose service. 9222 is its port. Replace both with your own. Chrome loadsbase_urlitself, so set it to an address Chrome can reach, as step 3 explains. -
Start a headless Chrome that Behat reaches at
api_urland that reaches yourbase_urlin turn. Any image that exposes a DevTools endpoint works, such aschromedp/headless-shell. When your site runs in Docker Compose, a service like this one starts a headless Chrome beside it:services:chrome:image: chromedp/headless-shell:latestshm_size: 1gbA container in the same Compose project, such as the one Behat runs in, reaches Chrome at
http://chrome:9222, theapi_urlin step 2. Chrome reaches the site by the site's service name in turn. Sobase_urluses that name and the port the site listens on inside the Compose network, such ashttp://nginx:8080, never a port published to your machine.shm_sizegives Chrome more shared memory than Docker's 64 MB default, which a large page can run out of. This repository runs the same image as itschrome_headlessservice in docker-compose.yml, which its own tests reach athttp://chrome_headless:9222.
Confirm that Behat reaches Chrome the first time you run a scenario tagged @javascript. An error that starts Could not fetch version information from http://chrome:9222/json/version means Behat can't reach api_url.
Watch the browser in a headed Chrome
When Behat runs on your machine, you can watch the browser in a headed Chrome, one with a window. You need the chrome session, the ChromeExtension registration and the use lines from Run JavaScript scenarios in headless Chrome. To watch the browser in a headed Chrome:
-
Start Chrome on your machine with
--remote-debugging-port=9222and a--user-data-dirof its own. -
In
behat.php, add a profile that pointsapi_urlat that Chrome, and replace the file'sreturnline with the snippet's, which registers both profiles:use Behat\Config\Config;use Behat\Config\Profile;// Runs with "vendor/bin/behat -p headed".$headed = (new Profile('headed'))->withExtension(new Extension(MinkExtension::class, ['sessions' => ['chrome' => ['chrome' => ['api_url' => 'http://localhost:9222']],],]));return (new Config())->withProfile($profile)->withProfile($headed);A profile other than
defaultstarts fromdefaultand overrides only what it sets. Soheadedcan registerMinkExtensionagain, and the headed run keeps every other setting.BEHAT_PARAMScan't make this change, because it can't override a settingbehat.phpmakes, as Environment variables explains. -
Run Behat with that profile:
vendor/bin/behat -p headed. Behat reaches Chrome athttp://localhost:9222, and Chrome has to reach your site'sbase_urlin turn. Confirm that Chrome loads the pages of each@javascriptscenario in a window of its own.
When Behat runs in a container, localhost is the container itself, and a Chrome on your machine can't open a Compose address such as http://nginx:8080. To watch a browser in that setup, run @javascript scenarios through Selenium, as Run JavaScript scenarios through Selenium shows. Use an image that shows its browser in a web page, such as selenium/standalone-chromium. In your own browser, open http://localhost: followed by the port docker compose port selenium 7900 prints, and enter the image's default password, secret.
Run JavaScript scenarios through Selenium
lullabot/mink-selenium2-driver is a fork of behat/mink-selenium2-driver that speaks the W3C WebDriver protocol. The fork provides the same Selenium2Driver class and declares that it replaces the original, so Composer installs only 1 of them. To run @javascript scenarios through a Selenium server:
-
Add
lullabot/mink-selenium2-driver:composer require --dev lullabot/mink-selenium2-driverIf your project requires the original
behat/mink-selenium2-driver, run these commands instead, because a project can't require both:composer remove --dev behat/mink-selenium2-drivercomposer require --dev lullabot/mink-selenium2-driver -
In
behat.php, give Mink aselenium2session that points at your Selenium server, and setjavascript_sessionto it:$profile->withExtension(new Extension(MinkExtension::class, ['base_url' => 'http://your-site.local','browser_name' => 'chrome','javascript_session' => 'selenium2','sessions' => ['browserkit_http' => ['browserkit_http' => NULL],'selenium2' => ['selenium2' => ['wd_host' => 'http://selenium:4444/wd/hub',]],],]));wd_hostis the Selenium server's address. In it,seleniumis the server's host name, such as the name of its Docker Compose service, and 4444 is the portselenium/standalone-chromiumlistens on.browser_nameis the browser it starts. The browser loadsbase_urlitself, so set it to an address that browser can reach. behat.dist.php sets the session up the same way. -
Start a Selenium server that Behat reaches at
wd_host. When your site runs in Docker Compose, a service like this one starts it beside the site:services:selenium:image: selenium/standalone-chromium:latestshm_size: 1gbports:- "7900"With
ports, Docker publishes the page that shows the browser, on port 7900, to a free port of your machine.docker compose port selenium 7900prints that port. Leaveportsout when you don't watch the browser. This repository runs the same image as itschromeservice in docker-compose.yml, and its own tests reach that Selenium server athttp://chrome:4444/wd/hub.
Confirm that your project has the fork: composer show lullabot/mink-selenium2-driver prints the fork's details when your project has it, and Package "lullabot/mink-selenium2-driver" not found when it doesn't.
Where to next
- The Quick start takes you from an empty
behat.phpto a passing scenario. - If your suite already has contexts of its own, Contexts shows which Behat Steps context your
FeatureContextextends and where your own steps go.