Por qué su batería de pruebas tarda once minutos
Una batería lenta es una batería que la gente se salta. El tiempo casi nunca está en las aserciones: está en reconstruir la base de datos, hashear contraseñas y salir a la red sin querer.
Una batería que tarda once minutos no se ejecuta antes de hacer push. La ejecuta CI, después de que se haya perdido el contexto, lo que convierte una corrección de cinco segundos en un viaje de ida y vuelta de veinte minutos. Al final alguien hace push sobre una compilación en rojo porque está bastante seguro de que no tiene que ver.
La velocidad de la batería no es, por tanto, una comodidad para el equipo. Es lo que decide si las pruebas hacen su trabajo.
Medir antes de adivinar
php artisan test --profileEsto imprime las pruebas más lentas. Casi siempre un puñado de casos se lleva la mayor parte del reloj, y el arreglo es concreto y no arquitectónico.
La base de datos, que suele ser casi todo
Migrar en cada prueba. RefreshDatabase envuelve cada prueba en una
transacción y la revierte, lo cual es rápido. DatabaseMigrations ejecuta todas
las migraciones en cada prueba, lo cual no lo es. En un proyecto con doscientas
migraciones, esa diferencia es el problema entero.
Migraciones en lugar de un volcado de esquema. Incluso una vez por ejecución, repetir doscientas migraciones tarda más que cargar su resultado:
php artisan schema:dump --pruneLa batería carga entonces un solo fichero SQL. Es la mayor ganancia en una base de código madura y cuesta un comando.
Factories que crean más de lo que la prueba necesita. Una factory con una
cadena de has() creando veinte registros relacionados para una prueba que lee
un campo. Cree el mínimo, y use make() en lugar de create() donde nada toque
la base de datos.
El hasheo de contraseñas, que nadie se espera
El hasheo es lento a propósito: para eso está. Una batería que crea cientos de usuarios mediante una factory paga ese coste cientos de veces.
// tests/TestCase.php o Pest.php
Hash::driver('bcrypt')->setRounds(4);En baterías con muchos usuarios de prueba, esto solo ha partido tiempos de ejecución por la mitad.
Pruebas que salen a la red
La categoría más dañina, porque son lentas y poco fiables a la vez. Una prueba que llama a una API real espera al servidor de otra persona y falla cuando ese servidor tiene un mal día.
Http::preventStrayRequests();Ponga eso en el caso de prueba base. Cualquier prueba que haga una llamada HTTP sin simular falla ahora al instante y le dice cuál, y se va a encontrar algunas que no sabía que existían.
Lo mismo vale para colas, correo y almacenamiento. Simulados son instantáneos; reales están haciendo un trabajo sobre el que la prueba no afirma nada.
Esperas y sondeos
Un sleep(2) en una prueba que espera algo asíncrono son dos segundos en cada
ejecución, para siempre, y o sobra o - en una máquina más lenta - no basta.
Los ayudantes de tiempo de Laravel hacen innecesaria la espera: congele el tiempo, viaje hacia delante, afirme. Para trabajo genuinamente asíncrono, ejecute la cola de forma síncrona en las pruebas en lugar de esperar a un worker.
Las pruebas de navegador, que van en otro cubo
Son lentas porque conducen un navegador real, y no hay truco que haga eso rápido. La respuesta es la cantidad y no la velocidad: un número pequeño que cubra los caminos que importan, ejecutado aparte de la batería rápida, para que no se interpongan entre una persona y su realimentación.
El paralelismo, cuando las pruebas se lo merezcan
php artisan test --parallelMejora casi lineal en una máquina con varios núcleos, y va a dejar al descubierto todas las pruebas que dependían calladamente de otra. Ficheros de fixtures compartidos, ids fijos en el código, un registro que se da por existente porque lo creó una prueba anterior.
Esos fallos merece la pena tenerlos. Una prueba que solo pasa cuando se ejecuta después de otra no es una prueba, es una secuencia, y iba a fallar al azar en CI de todas formas.
El objetivo
Por debajo del minuto para la batería que la gente ejecuta antes de hacer push, con todo lo más lento - pruebas de navegador, integración contra servicios reales - en un trabajo aparte que se ejecute después.
La medida no es la cobertura ni la elegancia. Es si ejecutar las pruebas es algo que alguien hace sin tener que decidirlo.
Una suite que nadie ejecuta es también una suite detrás de la cual nadie puede actualizar. Por eso construirla es la primera fase de una actualización y no la última. Y si la suite rápida corre sobre SQLite mientras producción corre MySQL, ese intercambio tiene un coste, y no siempre es pequeño.
