bytes-commerce/behat-symfony-extension
## Getting Started
### Minimal Setup
1. **Install the package**:
```bash
composer require bytes-commerce/behat-symfony-extension
behat.yml):
extensions:
BytesCommerce\Behat\SymfonyExtension:
kernel:
path: "%paths.base%/config/kernel.php" # Path to your Symfony kernel
mink:
sessions:
default:
symfony: ~
src/Context/FeatureContext.php):
namespace App\Context;
use Behat\Behat\Context\Context;
use Symfony\Component\HttpKernel\KernelInterface;
final class FeatureContext implements Context
{
public function __construct(private KernelInterface $kernel) {}
/**
* @Given I access the homepage
*/
public function iAccessTheHomepage(): void
{
$response = $this->kernel->getContainer()->get('router')->generate('homepage');
// Assertions or further logic
}
}
config/services.yaml):
services:
App\Context\FeatureContext:
tags: ['behat.context']
./vendor/bin/behat
Test a Symfony route without spinning up a server:
Feature: Homepage
Scenario: Load homepage
Given I access the homepage
Then the response status code should be 200
KernelInterface, EntityManagerInterface).
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
#[Autoconfigure]
class UserContext implements Context {
public function __construct(private UserRepository $userRepo) {}
}
behat.context tag to auto-discover contexts.
services:
App\Context\UserContext:
tags: ['behat.context']
mink:
sessions:
default:
symfony: ~
FriendsOfBehat/PageObjectExtension:
use FriendsOfBehat\PageObjectExtension\Page\Page;
class LoginPage extends Page {
public function submitLogin(): void { /* ... */ }
}
Register the page in a context:
use FriendsOfBehat\PageObjectExtension\Page\CrawlerAwarePageInterface;
class LoginContext implements Context, CrawlerAwarePageInterface {
public function getPage(): LoginPage { /* ... */ }
}
BrowserKitDriver for non-UI tests:
mink:
sessions:
browserkit:
browserkit: ~
Access the container in contexts:
use Symfony\Component\HttpFoundation\Request;
class ApiContext implements Context {
public function __construct(private RequestStack $requestStack) {}
public function iSendARequest(): void {
$request = $this->requestStack->getCurrentRequest();
// Assertions...
}
}
Feature-Driven Development:
Scenario: Submit a contact form
Given I am on the contact page
When I fill the form with:
| name | email |
| John | john@example.com |
And I press "Submit"
Then I should see "Thank you!"
class ContactContext implements Context {
public function __construct(private ContactFormHandler $formHandler) {}
public function iFillTheForm(array $data): void {
$this->formHandler->submit($data);
}
}
Dependency Injection:
class UserContext implements Context {
public function __construct(private UserRepository $repo) {}
public function iCreateAUser(): void {
$this->repo->save(new User());
}
}
Shared State:
BeforeScenario/AfterScenario hooks to manage test state:
use Behat\Behat\Hook\Scope\ScenarioScope;
class HookContext implements Context {
public function beforeScenario(ScenarioScope $scope): void {
$container = $scope->getEnvironment()->getKernel()->getContainer();
$container->get('doctrine')->getConnection()->beginTransaction();
}
public function afterScenario(ScenarioScope $scope): void {
$container = $scope->getEnvironment()->getKernel()->getContainer();
$container->get('doctrine')->getConnection()->rollBack();
}
}
Kernel Path Configuration:
kernel.path in behat.yml points to your Symfony kernel file (e.g., config/kernel.php).No kernel found → Verify the path and file existence.Mink Session Defaults:
symfony: driver, ensure BrowserKitDriver is installed (auto-checked by the extension).composer require --dev friends-of-behat/mink-browserkit-driver
Context Initialization:
behat.context must be public and instantiable by Symfony’s DI container.Context "App\Context\PrivateContext" is not instantiable → Make the class public and constructor public.Circular Dependencies:
getService().Symfony 5+ Cache:
php bin/console cache:clear
Enable Debug Mode:
debug: true in behat.yml to see detailed error traces:
extensions:
BytesCommerce\Behat\SymfonyExtension:
debug: true
Inspect the Kernel:
public function __construct(KernelInterface $kernel) {
if (!$kernel->isDebug()) {
throw new \RuntimeException('Kernel must be in debug mode!');
}
}
Mink Session Dumps:
dump() in Mink contexts to inspect the DOM:
use FriendsOfBehat\MinkExtension\Context\MinkContext;
class MyMinkContext extends MinkContext {
public function someStep(): void {
$this->getSession()->dump();
}
}
Service Container Access:
public function __construct(KernelInterface $kernel) {
$container = $kernel->getContainer();
$logger = $container->get('logger');
$logger->info('Test started');
}
Overriding Mink Parameters:
mink_parameters in behat.yml:
extensions:
BytesCommerce\Behat\SymfonyExtension:
mink_parameters:
base_url: 'http://localhost:8000'
Multiple Sessions:
mink:
sessions:
default:
symfony: ~
admin:
symfony: ~
$this->getSession('admin')->visit($this->locatePath('/admin'));
Environment-Specific Config:
# config/packages/behat.yaml
behat:
kernel_path: '%kernel.project_dir%/config/kernel.php'
ContextInitializer to modify contexts before execution:
use BytesCommerce\Behat\SymfonyExtension\Context\ContextInitializerInterface;
class DebugInitializer implements ContextInitializerInterface {
public function initialize(object $context): void {
if ($context instanceof FeatureContext) {
$context->setDebugMode(true);
}
}
}
behat.yml:
extensions:
BytesCommerce\Behat\SymfonyExtension:
How can I help you explore Laravel packages today?