Behat es el framework de BDD (Behavior-Driven Development) de referencia en PHP. Te permite escribir las pruebas en lenguaje natural —lo que se conoce como Gherkin— y luego conectar cada frase con código que ejecuta y comprueba el comportamiento real de tu aplicación. El resultado son pruebas que cualquiera puede leer y que documentan lo que el sistema debería hacer.
En esta guía montamos Behat desde cero sobre un proyecto Symfony 8.1, escribimos nuestra primera feature en español y la ejecutamos contra la aplicación sin necesidad de levantar un servidor ni un navegador.

Antes de empezar
Para esta practica usaremos:
Symfony 8.1 (la versión estable actual desde mayo de 2026).
PHP 8.4 o superior, que es el mínimo que exige Symfony 8.1.
Composer actualizado.
Docker (Proximamente)
Vamos a comenzar con un proyecto PHP/Symfony instalado en el equipo host. Comprueba tu versión de PHP:
bash
php -v
Y que el proyecto arranca:
bash
symfony console about
1. Instalar Behat y la extensión de Symfony
La integración oficial y mantenida entre Behat y Symfony es friends-of-behat/symfony-extension. (El antiguo behat/symfony2-extension está archivado; no lo uses.)
bash
composer require --dev behat/behat friends-of-behat/symfony-extension
Un detalle importante de versiones: el soporte para Symfony 8 llegó en la versión 2.7 de la extensión. Composer resolverá automáticamente a ^2.7 porque las versiones anteriores solo admiten Symfony 6.4/7.x, pero conviene tenerlo presente si trabajas con un composer.lock antiguo.
Para las pruebas funcionales (visitar rutas, comprobar respuestas HTML) añadimos también Mink, la capa de abstracción de navegador de Behat:
bash
composer require --dev friends-of-behat/mink-extension
No hace falta instalar un driver de navegador aparte: la propia SymfonyExtension incluye un driver de Mink llamado symfony que ejecuta las peticiones directamente contra el kernel de tu aplicación. Es rápido y no necesita ni servidor web ni Chrome.
2. Preparar el fichero de arranque
Behat necesita un fichero de bootstrap que cargue las variables de entorno de Symfony antes de arrancar el kernel. En Symfony moderno ese fichero es tests/bootstrap.php, y viene con el pack de pruebas. Si aún no lo tienes:
bash
composer require --dev symfony/test-pack
Eso crea tests/bootstrap.php con un contenido parecido a este:
php
<?php use Symfony\Component\Dotenv\Dotenv; require dirname(__DIR__).'/vendor/autoload.php'; if (method_exists(Dotenv::class, 'bootEnv')) { (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); }
Symfony ya incluye un .env.test que fija APP_ENV=test, de modo que Behat se ejecutará en el entorno de pruebas.
3. Crear el fichero de configuración behat.dist.yaml
En la raíz del proyecto crea behat.dist.yaml. La convención es versionar el .dist y dejar que cada persona pueda sobrescribirlo con un behat.yaml local (que sí deberías ignorar en Git).
yaml
# behat.dist.yaml default: suites: default: contexts: - App\Tests\Behat\FeatureContext extensions: FriendsOfBehat\SymfonyExtension: bootstrap: tests/bootstrap.php kernel: class: App\Kernel Behat\MinkExtension: base_url: 'http://localhost' sessions: symfony: symfony: ~
Qué hace cada bloque:
SymfonyExtensionarranca tu kernel (App\Kernel) usando el bootstrap que preparamos.MinkExtensiondefine una sesión llamadasymfonyque usa el driver interno de la extensión. Ese driver dispara las peticiones contra el kernel, sin red de por medio.
4. Escribir tu primer contexto
Un contexto es la clase que traduce las frases de Gherkin en código PHP. Crea tests/Behat/FeatureContext.php.
Behat moderno usa atributos de PHP (#[Given], #[When], #[Then]) para enlazar cada método con su frase. Las viejas anotaciones en docblock (@When) siguen funcionando, pero están deprecadas, así que usamos atributos.
php
<?php declare(strict_types=1); namespace App\Tests\Behat; use Behat\MinkExtension\Context\RawMinkContext; use Behat\Step\Then; use Behat\Step\When; use PHPUnit\Framework\Assert; final class FeatureContext extends RawMinkContext { #[When('visito la página :ruta')] public function visitoLaPagina(string $ruta): void { $this->visitPath($ruta); } #[Then('la respuesta debe ser correcta')] public function laRespuestaDebeSerCorrecta(): void { Assert::assertSame(200, $this->getSession()->getStatusCode()); } #[Then('debería ver el texto :texto')] public function deberiaVerElTexto(string $texto): void { $this->assertSession()->pageTextContains($texto); } }
Al extender RawMinkContext heredamos utilidades muy cómodas: visitPath() para navegar, getSession() para acceder a la respuesta y assertSession() para aserciones sobre el contenido de la página.
5. Escribir la primera feature en español
Gherkin está traducido a decenas de idiomas. Basta con abrir el fichero con la directiva # language: es y ya podemos usar Característica, Escenario, Cuando, Entonces y demás palabras clave en castellano.
Crea features/inicio.feature:
gherkin
# language: es Característica: Página de inicio Para asegurarme de que el sitio está en pie Como visitante Quiero poder abrir la página de inicio Escenario: La home responde correctamente Cuando visito la página "/" Entonces la respuesta debe ser correcta Y debería ver el texto "Bienvenido"
Ajusta la ruta y el texto esperado a lo que realmente devuelva tu aplicación.
6. Ejecutar Behat
bash
php vendor/bin/behat
Si todo está bien conectado, verás el escenario en verde. Un par de comandos útiles:
bash
# Ver qué pasos (steps) hay definidos php vendor/bin/behat -dl # Ejecutar solo una feature concreta php vendor/bin/behat features/inicio.feature
Cuando escribes una frase en el .feature que aún no tiene método asociado, Behat te la marca como undefined y te ofrece generar automáticamente el esqueleto del método. Es la forma natural de trabajar: primero describes el comportamiento, luego lo implementas.
7. Ir más allá: inyectar servicios de Symfony en el contexto
Tarde o temprano querrás usar servicios de tu aplicación dentro de las pruebas: un repositorio de Doctrine para preparar datos, el EntityManager para limpiar la base entre escenarios, un servicio propio, etc.
La SymfonyExtension resuelve el contexto desde el contenedor de servicios si está registrado como servicio en el entorno test. Añade a config/services_test.yaml:
yaml
# config/services_test.yaml services: _defaults: autowire: true autoconfigure: true App\Tests\Behat\: resource: '../tests/Behat/'
Con eso, el autowiring está activo y puedes pedir dependencias por el constructor:
php
use Doctrine\ORM\EntityManagerInterface; public function __construct( private readonly EntityManagerInterface $entityManager, ) { }
A partir de ahí, un hook como #[BeforeScenario] te sirve para dejar la base de datos en un estado conocido antes de cada prueba.
Notas de versiones y siguientes pasos
Behat 3.x es la rama estable recomendada. La versión 4.0 está en fase alpha (desde mediados de 2026); si buscas estabilidad para producción, quédate en la 3.x por ahora.
friends-of-behat/symfony-extension2.7 o superior es imprescindible para Symfony 8; las 2.6.x no lo admiten.Behat también admite un fichero de configuración en PHP (
behat.dist.php) con la API fluidaBehat\Config\Config, como alternativa al YAML. El YAML sigue siendo la vía más documentada para las extensiones.Para pruebas con JavaScript o navegador real (SPA, contenido que se renderiza en cliente), el driver
symfonyno basta: ahí entran Symfony Panther o un driver de Chrome/Selenium para Mink.
Con esto tienes una base sólida de BDD sobre Symfony 8.1: escribes el comportamiento esperado en lenguaje claro, lo conectas con contextos, y dejas que Behat verifique que tu aplicación cumple lo prometido.
Proyectos relacionados:
Symfony — el sistema sobre el que se construye la aplicación: symfony.com
Behat — el proyecto de pruebas y su documentación: behat.org
SymfonyExtension (Friends of Behat) — la pieza que conecta ambos: github.com/FriendsOfBehat/SymfonyExtension