behat/mink-zombie-driver
Mink driver powered by Zombie.js for fast, headless browser testing with Node.js. Install Zombie via npm (v2+) and the driver via Composer, then run Mink sessions against real pages to interact with DOM, click, fill forms, and assert content.
To integrate behat/mink-zombie-driver into a Laravel project, follow these steps:
Install Dependencies Ensure Node.js (v12+) and npm are installed globally. Install Zombie.js:
npm install -g zombie@2.0.0
Install PHP dependencies via Composer:
composer require --dev behat/mink behat/mink-zombie-driver
Configure Mink in Laravel
Create a custom test helper (e.g., tests/TestHelper.php) to initialize Mink with the Zombie driver:
use Behat\Mink\Mink;
use Behat\Mink\Session;
use Behat\Mink\Driver\ZombieDriver;
use Behat\Mink\Driver\NodeJS\Server\ZombieServer;
function createZombieSession(): Session {
$zombieServer = new ZombieServer('127.0.0.1', 8124, '/usr/local/bin/node');
$driver = new ZombieDriver($zombieServer);
$session = new Session($driver);
$session->start();
return $session;
}
First Test Case Use the session in a PHPUnit test:
use Behat\Mink\Session;
class ExampleTest extends \Tests\TestCase {
public function testZombieDriver() {
$session = createZombieSession();
$session->visit('http://localhost:8000');
$this->assertEquals('Home', $session->getPage()->find('css', 'h1')->getText());
$session->quit();
}
}
Run Tests Execute tests with PHPUnit:
php artisan test --testdoxhtml
tests/TestHelper.php for session management.Test dynamic form behavior (e.g., AJAX submissions):
$session = createZombieSession();
$session->visit('/login');
$session->getPage()->fillField('email', 'user@example.com');
$session->getPage()->fillField('password', 'secret');
$session->getPage()->pressButton('Login');
$this->assertStringContainsString('Welcome', $session->getPage()->getText());
Execute JS and assert results:
$session->getPage()->executeScript('alert("Hello")');
$this->assertEquals('Hello', $session->evaluateScript('return window.prompt("Enter:")'));
Wait for AJAX-loaded content:
$session->wait(3000); // Wait 3 seconds (use sparingly)
$this->assertTrue($session->getPage()->find('css', '.dynamic-content')->isVisible());
Extend a FeatureContext:
use Behat\MinkExtension\Context\MinkContext;
class CustomContext extends MinkContext {
protected function getSession(): Session {
return createZombieSession();
}
}
Feature file example:
Scenario: Login via form
Given I am on "/login"
When I fill in "email" with "user@example.com"
And I fill in "password" with "secret"
And I press "Login"
Then I should see "Welcome"
Reuse the same Zombie server across tests (but avoid race conditions):
$session1 = createZombieSession();
$session2 = createZombieSession();
$session1->visit('/page1');
$session2->visit('/page2');
ZombieServer in a custom command for debugging:
use Symfony\Component\Process\Process;
class ZombieDebugCommand extends Command {
protected function handle() {
$process = new Process(['node', 'path/to/zombie', 'http://localhost:8000']);
$process->start();
$this->info('Zombie server running...');
}
}
FROM node:12
RUN npm install -g zombie@2.0.0
$session->reset();
Zombie.js Limitations
localStorage, Service Workers).wait() cautiously).Configuration Quirks
8124 by default) is free./usr/local/bin/node may fail on Windows/CI. Use:
$nodeBinary = exec('which node'); // Linux/macOS
// OR
$nodeBinary = 'C:\Program Files\nodejs\node.exe'; // Windows
$session->quit().Debugging Tips
$zombieServer = new ZombieServer('127.0.0.1', 8124, $nodeBinary, [
'debug' => true,
]);
zombie CLI to test pages:
zombie http://localhost:8000
$session->getPage()->executeScript('console.log("Test")');
Common Errors
npm -g list zombie).wait() or executeScript to trigger dynamic content:
$session->executeScript('document.querySelector(".dynamic").style.display = "block"');
sockets extension is enabled (php -m | grep sockets).Custom Zombie Scripts Override the default Zombie server script (e.g., for custom headers):
$zombieServer = new ZombieServer(
'127.0.0.1',
8124,
$nodeBinary,
[],
__DIR__ . '/custom-zombie-script.js'
);
Example custom-zombie-script.js:
var Browser = require('zombie');
var browser = new Browser({ headers: { 'X-Custom-Header': 'test' } });
browser.visit(process.argv[2]);
console.log(browser.html());
Mink Event Listeners Extend Mink to log interactions:
$mink = new Mink();
$mink->getSession()->getDriver()->addListener(new class {
public function onVisit(VisitEvent $event) {
\Log::info('Visited: ' . $event->getUrl());
}
});
Laravel Service Provider Register Mink/Zombie as a Laravel service:
use Behat\Mink\Mink;
class MinkServiceProvider extends ServiceProvider {
public function register() {
$this->app->singleton(Mink::class, function () {
return new Mink([ 'zombie' => createZombieSession() ]);
});
}
}
evaluateScript for Debugging:
$session->evaluateScript('console.log(JSON.stringify(document.body.innerHTML))');
$session->getPage()->executeScript(<<<JS
window.fetch
How can I help you explore Laravel packages today?