Installation Add the package via Composer:
composer require --dev friends-of-behat/mink
For Laravel, use require-dev to avoid production bloat.
Basic Configuration
In composer.json, define a mink section under extra:
"extra": {
"mink": {
"default_session": "selenium2",
"sessions": {
"selenium2": {
"goutte": false,
"selenium": {
"wdf": true,
"browser": "chrome"
}
}
}
}
}
First Use Case: Testing a Login Flow
Create a FeatureContext class (e.g., tests/FeatureContext.php):
use Behat\MinkExtension\Context\MinkContext;
use FriendsOfBehat\Mink\Driver\Selenium2Driver;
class FeatureContext extends MinkContext {
public function __construct() {
$this->useMinkSession('selenium2');
}
}
Write a .feature file:
Feature: Login
Scenario: Successful login
Given I am on "/login"
When I fill in "email" with "user@example.com"
And I fill in "password" with "secret"
And I press "Submit"
Then I should see "Welcome, User"
Session Management
useMinkSession() in BeforeScenario hooks to switch contexts (e.g., selenium2 for JS, goutte for headless).$this->getSession()->start();
$this->getSession()->switchToIFrame('iframe-id'); // For embedded content
Laravel-Specific Integration
Mink in AppServiceProvider:
public function register() {
$this->app->singleton(Mink::class, function ($app) {
return new Mink(new \FriendsOfBehat\Mink\Session($app['mink.session']));
});
}
Mink to test API routes via HttpSession:
$session = $this->getSession();
$session->visit('/api/login');
$response = $session->getPage()->getContent();
Data-Driven Tests
Given/When steps:
/**
* @Given I am logged in as :user
*/
public function iAmLoggedInAs($user) {
$this->visit('/login');
$this->fillField('email', $user['email']);
$this->pressButton('Login');
}
Debugging Tools
$this->getSession()->getDriver()->takeScreenshot('screenshot.png');
$element = $this->getSession()->getPage()->find('css', '.error');
$this->log((string) $element->getOuterHtml());
Session Initialization
SessionNotCreatedException if Selenium WebDriver isn’t running.docker run -d -p 4444:4444 selenium/standalone-chrome
php artisan mink:server command to manage the session lifecycle.Dynamic Content
ElementNotFoundException for AJAX-loaded content.$this->getSession()->wait(5000, 1000, function () {
return $this->getSession()->getPage()->find('css', '.dynamic-content') !== null;
});
Headless Mode
wdf (WebDriverFactory) explicitly:
"selenium": {
"wdf": {
"host": "http://localhost",
"port": 4444,
"browser": "chrome",
"desired_capabilities": {
"chromeOptions": {
"args": ["--headless", "--disable-gpu"]
}
}
}
}
Symfony Dependency
Mink expects Symfony components (e.g., HttpKernel).$kernel = new \Illuminate\Foundation\Application();
$this->getSession()->setKernel($kernel);
Custom Drivers
Selenium2Driver for Laravel-specific needs:
class LaravelSeleniumDriver extends Selenium2Driver {
public function loginAs(User $user) {
$this->visit('/login');
$this->fillField('email', $user->email);
$this->pressButton('Login');
}
}
Performance
$this->getSession()->start(); // Once in @BeforeScenario
CI/CD
goutte for fast, headless tests in CI:
"sessions": {
"goutte": {
"goutte": { "guzzle_version": "~6.0" }
}
}
selenium2 for JS-heavy features.Debugging
$this->getSession()->getDriver()->setLogLevel(\Monolog\Logger::DEBUG);
php artisan mink:debug (if available) to inspect sessions.Extension Points
/**
* @Given I am on the :page page
*/
public function iAmOnThePage($page) {
$this->visit(route($page));
}
BeforeScenario/AfterScenario for setup/teardown:
/**
* @BeforeScenario
*/
public function ensureSession() {
$this->getSession()->start();
}
How can I help you explore Laravel packages today?