Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Extension Laravel Package

friends-of-behat/symfony-extension

Behat extension that integrates Symfony 6 and 7 with a Mink driver, letting you define contexts as autowired Symfony services and test your application without setting up a server.

View on GitHub
Deep Wiki
Context7

Getting Started

If you're coming from Laravel, the mental model is: Behat = Pest/PHPUnit, SymfonyExtension = Laravel's $this->get() HTTP testing layer + service container access, Mink symfony driver = a built-in KernelBrowser instead of Dusk. No web server, no Selenium, no php artisan serve — the kernel is booted in-process.

Minimal steps:

composer require --dev \
  behat/behat \
  behat/mink \
  behat/mink-extension \
  behat/mink-browserkit-driver \
  friends-of-behat/symfony-extension:^2.6

(Pin the minor deliberately — see Gotchas. The symfony Mink driver is not usable without a BrowserKit driver; the extension hard-fails with a safety check if it's missing.)

behat.yml at project root:

default:
  suites:
    default:
      contexts:
        - App\Tests\Behat\FeatureContext
  extensions:
    FriendsOfBehat\SymfonyExtension:
      kernel:
        class: App\Kernel
        environment: test
        debug: true
    Behat\MinkExtension:
      sessions:
        default:
          symfony: ~

Register the context as a regular Symfony service in config/services_test.yaml (this is the whole point — the context is resolved from the container, not new-ed by Behat):

services:
  _defaults:
    autowire: true
    autoconfigure: true

  App\Tests\Behat\:
    resource: '../../tests/Behat/*'
#[Given('I visit the homepage')]
public function iVisitTheHomepage(): void
{
    $this->mink->getSession()->visit('/');
}

Run: vendor/bin/behat.

Where to look first: DOCUMENTATION.md — specifically Installation, Usage, Mink integration, and the Configuration reference. The features/ directory in the package is its own executable contract; if you want to know whether a behaviour is supported in your version, read the scenarios there rather than the prose.

First use case that pays off immediately: a smoke test that boots the app, hits /, and asserts the response plus a database row created through your real domain services — the exact job Laravel's RefreshDatabase + $this->get() does, but written in Gherkin so product can read it.

Implementation Patterns

Contexts as services — inject what you'd app() in Laravel. Repositories, entity managers, mailers, clocks, HttpClient mocks, feature flags: all constructor-injected and autowired.

final class RegistrationContext implements Context
{
    public function __construct(
        private readonly UserRepository $users,
        private readonly Mink $mink,
    ) {}
}

Arrange through services, act through HTTP. Mirrors the Laravel habit of User::factory() for setup and $this->post() for the assertion target. It also keeps scenarios fast and independent of HTML.

Page objects for the UI layer. With friends-of-behat/page-object-extension installed, page objects are autowired into contexts the same way — treat them like Laravel's Tests\Browser\Pages.

Compose contexts instead of a god-context. Since v2.0.5 one context may inject another, so FeatureContext can hold DatabaseContext, AuthContext, EmailContext and delegate step definitions. Keep the autowired constructor of each small; use bind in services_test.yaml for scalar config (API endpoints, timeouts, fixture paths).

Transactional isolation per scenario via hooks, since the package does not manage your database:

#[BeforeScenario]
public function beginTransaction(): void { $this->em->beginTransaction(); }

#[AfterScenario]
public function rollBack(): void { $this->em->rollback(); $this->em->clear(); }

If you go this route, make sure your kernel isn't rebooting a separate container between scenarios (see Gotchas).

Feature files as living documentation per bounded context, one suite per area (admin, api, storefront) rather than one giant suite — suites are cheap to add and let you run behat --suite=api in watch loops, the equivalent of php artisan test --filter.

CI recipe: compile the container once (bin/console cache:warmup --env=test) then run vendor/bin/behat --format=progress --no-colors --stop-on-failure. It's noticeably faster than PHPUnit feature tests since there's no HTTP round-trip and no server process to boot.

Gotchas and Tips

  • Version pinning is a real trap. The latest line (v2.7.0) dropped PHP < 8.3 and Symfony < 7.4. On Symfony 6.4 / PHP 8.1–8.2 you must pin an earlier minor (^2.6), otherwise Composer resolves something that fails on install. Check composer show before filing bugs.
  • Never install behat/symfony2-extension alongside this. They conflict conceptually, and stale config keys (kernel.file vs kernel.path) from old blog posts will silently do nothing.
  • Stale test kernel cache is the #1 "why isn't my service injected?" cause. With debug: false the container is not rebuilt on config changes; clear var/cache/test (or run bin/console cache:clear --env=test) after touching services_test.yaml. This is the equivalent of Laravel's cached config manifest biting you.
  • Don't rely on Kernel::getContainer() semantics. Use the test container so private services are reachable, and ensure the test framework config is active in config/packages/test/framework.yaml. Reaching into the container by service id is a code smell in a context — autowire instead; it's the whole reason the package exists.
  • $minkParameters is not autoconfigured. Since v2.0.3 you cannot get it injected without a typehint; if a context needs Mink parameters, implement MinkAwareContext / use the MinkAware trait, or pass the values in via bind in services_test.yaml. Cleanest: inject Behat\Mink\Mink directly and ignore the parameters array.
  • No JavaScript, no real browser. The symfony driver is BrowserKit — links, forms, redirects, cookies. Anything needing JS requires a real Mink driver session (base_url to an external server), at which point you've lost the "no server setup" benefit.
  • There are no assertions. Behat ships none; use webmozart/assert, PHPUnit's Assert if it's installed, or hand-rolled throw new \RuntimeException. Laravel devs are often surprised there's no expectException equivalent out of the box.
  • Contexts must be in the suite's contexts list and registered as services — one without the other produces confusing "context not found" or "service not found" errors depending on which is missing.
  • Autowiring ambiguity: two services implementing one interface → bind explicitly in services_test.yaml. Same failure mode as Laravel's container throwing on unresolvable __construct typehints, but the messaging is worse.
  • Email assertions are not built in. Enable the profiler on the client from the driver and read the mailer collector, or install zenstruck/mailer-test and inject the mailbox into your context.
  • Debug flow: behat -vvv --format=pretty --stop-on-failure, plus var/log/test.log when kernel.debug: true. dump() from Symfony VarDumper works and is far better than var_dump in step definitions.
  • Extension points worth knowing: kernel.bootstrap for custom kernel construction (e.g. loading .env.test.local yourself, like a custom bootstrap/app.php), a custom driver class if you need to wrap the kernel browser, and the PageObjectExtension integration for page-object autowiring.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core