El botón verde de "run test", pero en Emacs: ejecuta el escenario donde está el cursor
La última milla
Venía cerrando mi flujo de tests de Behat dentro de Emacs. Ya tenía dos piezas montadas en posts anteriores:
- Saltar del paso al código: cursor
sobre un
Given,gd, y caigo en el método PHP que lo implementa. - Lanzar un comando desde la raíz del
proyecto con
SPC p c, que recuerda lo que escribiste.
Faltaba la que más se usa, la del iconito verde del IDE: estar dentro de un escenario, pulsar una tecla, y que se ejecute solo ese. Sin escribir la ruta, sin acordarme de la línea, sin salir a la terminal.
El truco es el mismo de siempre: `fichero:línea`
Behat (como pytest, como casi todos) acepta que le digas exactamente
qué correr con la sintaxis fichero.feature:42 — "ejecuta el
escenario que empieza en la línea 42". Y el editor sabe en qué fichero
estás y en qué línea tienes el cursor. Une las dos cosas y ya está:
coges el fichero, pegas el número de línea, lanzas.
Las funciones
(defvar my/behat-command "bin/behat"
"Ejecutable de Behat, relativo a la raiz del proyecto.")
(defvar my/behat-default-args "--tags '~@pendiente'"
"Argumentos por defecto (ajusta a tu gusto, o cadena vacia).")
(defun my/behat--root ()
"Raiz del proyecto Behat: el dir ancestro que CONTIENE bin/behat.
Lo busca subiendo desde el fichero, asi acierta aunque trabajes desde un
proyecto-paraguas que agrupa varios servicios (ver mas abajo)."
(let ((file (or (buffer-file-name) default-directory)))
(expand-file-name
(or (locate-dominating-file
file (lambda (d) (file-exists-p (expand-file-name "bin/behat" d))))
(locate-dominating-file file "behat.yml")
default-directory))))
(defun my/behat--run (&optional line)
"Ejecuta Behat sobre el .feature del buffer, desde la raiz del proyecto.
Si LINE se da, corre solo el escenario que empieza ahi (fichero:LINE)."
(let* ((root (my/behat--root))
(rel (file-relative-name (buffer-file-name) root))
(target (if line (format "%s:%d" rel line) rel))
(default-directory root)) ; correr desde la raiz del servicio
(compile (string-trim (format "%s %s %s"
my/behat-command my/behat-default-args target)))))
(defun my/behat-run-scenario ()
"Ejecuta con Behat solo el escenario bajo el cursor."
(interactive)
(my/behat--run (line-number-at-pos)))
(defun my/behat-run-feature ()
"Ejecuta con Behat toda la feature del buffer."
(interactive)
(my/behat--run))
Tres ideas y se entiende solo:
my/behat--rootsube desde el fichero hasta el directorio que tienebin/behat, y atandodefault-directorya esa raízcompileejecuta desde ahí — necesario para quebin/behat(ruta relativa) exista. (Si lo lanzas desde la subcarpeta del.feature, no lo encuentra; esa piedra ya la conté en otro post.)line-number-at-poses la línea del cursor. La pegamos al fichero con ese:mágico para correr solo el escenario.compilelanza el comando y te deja la salida en un buffer donde cada fallo es un enlace: Enter encima y saltas al sitio.
Y un detalle que importa: compile hereda
el entorno del buffer. Si usas direnv/Nix (el módulo
envrc en Doom), el bin/behat sale con las
herramientas correctas en el PATH, igual que en tu terminal. Sin eso,
"no encuentra php" y a volverse loco.
Atarlo a una tecla (y de paso, adiós a Ruby)
(after! feature-mode
;; El runner propio de feature-mode venia apuntando a cucumber (Ruby).
;; Lo mandamos a Behat:
(setq feature-cucumber-command "bin/behat {options} \"{feature}\"")
(map! :map feature-mode-map
:localleader
"v" #'my/behat-run-scenario ; SPC m v -> escenario bajo el cursor
"V" #'my/behat-run-feature)) ; SPC m V -> feature entera
feature-mode (el modo de los .feature) trae
de fábrica comandos para correr escenarios, pero pensados para
Cucumber/Ruby. Con esa línea su ejecutor pasa a usar Behat. Y los dos
atajos nuevos son la guinda: cursor en un escenario,
SPC m v, y corre solo ese.
Por qué busco `bin/behat` y no "la raíz del proyecto"
Lo natural sería preguntarle a tu gestor de proyectos (en Emacs,
projectile) "¿cuál es la raíz?". Y funciona… hasta que tienes un
monorepo de microservicios: una carpeta con veinte
servicios, cada uno su repo, y una feature que toca tres a la vez. Si
marcas la carpeta de arriba como proyecto-paraguas para buscar y navegar
a lo ancho, "la raíz del proyecto" pasa a ser esa de arriba… donde
no hay ningún bin/behat.
Y el comando revienta con un "no such file".
Por eso my/behat--root no pregunta "¿cuál es el
proyecto?", sino "¿dónde está el bin/behat más cercano
subiendo?". Esa pregunta acierta siempre: te da el servicio concreto,
tengas activo el paraguas o el micro. La lección general: cuando un
comando necesita un fichero concreto, busca ese
fichero, no "el proyecto" — en cuanto el árbol se
complica, son cosas distintas.
El flujo completo, sin tocar la terminal
Ahora mi bucle de trabajo con Behat vive entero dentro del editor:
| Quiero… | Pulso… |
|---|---|
| Ir del paso al método PHP | gd |
| Ejecutar este escenario | SPC m v |
| Ejecutar la feature entera | SPC m V |
| Re-ejecutar lo último | g en el buffer de resultados |
| Saltar a un fallo | Enter sobre la línea del error |
Esa era la última cosa que me ataba al IDE para mi día a día de PHP. Y van unas cuantas tachadas de la lista. El patrón se repite tanto que ya casi me aburre decirlo: lo que crees que te falta no falta, está apagado o se monta en quince líneas. Un editor de los setenta, un poco de Elisp, y tu botón verde de "run test" hecho a tu medida.
— doomguru
Comentarios (0)
Sin comentarios todavia. Se el primero!
Deja un comentario