Saltar al contenido principal

Jest 30: Más rápido, más ligero, mejor

· 12 min de lectura
Svyatoslav Zaytsev
Svyatoslav Zaytsev
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Hoy nos complace anunciar el lanzamiento de Jest 30. Esta versión incluye una cantidad sustancial de cambios, correcciones y mejoras. Aunque es uno de los lanzamientos principales más grandes de Jest hasta la fecha, admitimos que tres años para una versión mayor es demasiado tiempo. En el futuro, nuestro objetivo es realizar lanzamientos principales más frecuentes para mantener Jest excelente durante la próxima década.

Si quieres saltarte las novedades y comenzar directamente, ejecuta npm install jest@^30.0.0 y sigue la guía de migración: Actualizar de Jest 29 a Jest 30.

¿Qué hay de nuevo?

Jest 30 es notablemente más rápido, consume menos memoria y viene con toneladas de nuevas características. Primero, veamos los cambios importantes que pueden romper tu código:

Cambios Rompedores

  • Jest 30 elimina el soporte para Node 14, 16, 19 y 21.

  • jest-environment-jsdom se actualizó de jsdom 21 a 26.

  • La versión mínima compatible de TypeScript es ahora 5.4.

  • Se eliminaron varios alias de expect. eslint-plugin-jest incluye un autofixer que puedes ejecutar para actualizar automáticamente tu base de código.

  • Las propiedades no enumerables de objetos ahora se excluyen por defecto en comparadores como toEqual.

  • Jest ahora admite archivos .mts y .cts por defecto.

  • --testPathPattern fue renombrado a --testPathPatterns.

  • Jest ahora maneja correctamente las promesas que primero son rechazadas y luego capturadas para evitar falsos positivos en las pruebas.

  • Realizamos varias mejoras en la impresión de snapshots de Jest que podrían requerir que actualices tus snapshots. Google descontinuó los enlaces goo.gl que estábamos usando en snapshots. Tampoco nos gusta, pero tendrás que actualizar todos tus snapshots.

  • Jest ahora se empaqueta en un único archivo por paquete. Esto mejora el rendimiento, pero podría romperse si creaste herramientas que acceden a los internos de Jest.

Estos son solo algunos aspectos destacados. La lista completa de cambios importantes está disponible en el CHANGELOG y la guía de migración de Jest 30.

Mejoras de rendimiento y memoria

Jest 30 ofrece mejoras de rendimiento tangibles gracias a muchas optimizaciones, especialmente en resolución de módulos, uso de memoria y aislamiento de pruebas. Al utilizar el nuevo unrs-resolver, la resolución de módulos en Jest se volvió más completa, compatible con estándares y más rápida. Agradecemos a @JounQin por la migración. Dependiendo de tu proyecto, podrías ver ejecuciones de pruebas significativamente más rápidas y menor consumo de memoria. Por ejemplo, una gran aplicación TypeScript con cliente y servidor observó un 37% más de velocidad en pruebas y un 77% menos de uso de memoria en parte de su base de código:

Jest 29Jest 30
Server tests~1350s / 7.8 GB max~850s / 1.8 GB max
Client tests~49s / 1.0 GB max~44s / 0.8 GB max

Jest es rápido, pero debido a su aislamiento de pruebas, el código de usuario lento a menudo exacerba los problemas de rendimiento y genera pruebas lentas. Cuando las pruebas dejan manejadores abiertos como temporizadores sin cerrar o conexiones a otros servicios, puede hacer que Jest se cuelgue o ralentice. Jest 30 ha mejorado en detectar y reportar estos problemas, lo que te ayuda a identificar y corregir pruebas lentas o problemáticas más fácilmente. Por ejemplo, las pruebas en Happo aceleraron un 50% pasando de 14 minutos a 9 minutos al limpiar manejadores abiertos y actualizar a Jest 30.

Si utilizas archivos que consolidan las exportaciones de múltiples módulos en un único archivo (conocidos como "barrel files"), te recomendamos usar herramientas como babel-jest-boost, babel-plugin-transform-barrels o no-barrel-file para evitar cargar grandes bloques de código de aplicación en cada archivo de prueba. Esto puede generar mejoras de rendimiento de hasta 100x.

Limpieza de variables globales entre archivos de prueba

Jest logra el aislamiento entre archivos de prueba ejecutando cada test en un contexto de VM separado, proporcionando a cada archivo un entorno global nuevo. Sin embargo, si tu código no limpia las variables globales después de cada archivo de prueba, puede provocar fugas de memoria en Jest y ralentizar tus ejecuciones. Jest 30 introduce una nueva funcionalidad que te alerta sobre variables globales no limpiadas correctamente tras cada ejecución.

En el futuro, Jest limpiará automáticamente las variables globales tras cada ejecución. Si no recibes advertencias sobre variables globales no limpiadas con Jest 30, ya puedes activar el modo de limpieza completa configurándolo en "on" para beneficiarte de importantes ahorros de memoria y mejoras de rendimiento:

export default {
testEnvironmentOptions: {
globalsCleanup: 'on',
},
};

El valor predeterminado en Jest es globalsCleanup: 'soft'. Para desactivar esta función puedes configurarlo en off. Si necesitas proteger objetos globales específicos de ser limpiados —por ejemplo, utilidades compartidas o cachés— puedes marcarlos como protegidos usando jest-util:

import {protectProperties} from 'jest-util';

protectProperties(globalThis['my-property']);

¡Gracias a @eyalroth por implementar esta funcionalidad!

Nuevas características

Mejoras en soporte para ECMAScript Modules y TypeScript

Se añadió soporte para import.meta.* y file:// al usar ESM nativo con Jest. Además, ahora puedes escribir tus archivos de configuración de Jest en TypeScript, y los archivos .mts y .cts son soportados nativamente sin configuración adicional. Si usas la función nativa de Node para eliminar tipos de TypeScript, ya no cargamos el transformador de TypeScript, acelerando las ejecuciones.

Spies y la palabra clave using

Ahora puedes usar la nueva sintaxis de gestión explícita de recursos de JavaScript (using) con spies de Jest. Si tu entorno lo soporta, escribir using jest.spyOn(obj, 'method') restaurará automáticamente el spy al terminar el bloque, evitando limpieza manual.

test('logs a warning', () => {
using spy = jest.spyOn(console, 'warn');
doSomeThingWarnWorthy();
expect(spy).toHaveBeenCalled();
});

Documentación

expect.arrayOf

Jest 30 introduce un nuevo comparador asimétrico, expect.arrayOf, que permite validar cada elemento de un array contra una condición o tipo. Por ejemplo, puedes verificar que un array contenga solo números:

expect(someArray).toEqual(expect.arrayOf(expect.any(Number)));

Documentación

Nuevo marcador de posición en test.each: %$

Si usas pruebas basadas en datos con test.each, ahora puedes incluir el marcador especial %$ en tus títulos para inyectar el número del caso de prueba. Por ejemplo:

test.each(cases)('Case %$ works as expected', () => {});

reemplazará %$ con el número secuencial de la prueba.

Documentación

jest.advanceTimersToNextFrame()

@sinonjs/fake-timers se actualizó a v13, añadiendo jest.advanceTimersToNextFrame(). Esta función avanza todas las llamadas pendientes de requestAnimationFrame al siguiente fotograma, facilitando las pruebas de animaciones o código que depende de requestAnimationFrame sin tener que adivinar las duraciones en milisegundos.

Documentación

Reintentos de pruebas configurables

Jest 30 mejora jest.retryTimes() con nuevas opciones que te permiten controlar cómo se manejan los reintentos. Puedes especificar un retraso o reintentar inmediatamente una prueba fallida en lugar de esperar a que termine todo el conjunto de pruebas:

// Retry failed tests up to 3 times, waiting 1 second between attempts:
jest.retryTimes(3, {waitBeforeRetry: 1000});

// Immediately retry without waiting for other tests to finish:
jest.retryTimes(3, {retryImmediately: true});

Documentación

jest.unstable_unmockModule()

Jest 30 añade una nueva API experimental jest.unstable_unmockModule() para un control más preciso al desactivar mocks de módulos (especialmente cuando se usa ESM nativo).

Documentación

jest.onGenerateMock(callback)

Se añadió un nuevo método onGenerateMock. Registra una función de callback que se invoca cada vez que Jest genera un mock para un módulo. Este callback te permite modificar el mock antes de que se devuelva a tu entorno de pruebas:

jest.onGenerateMock((modulePath, moduleMock) => {
if (modulePath.includes('Database')) {
moduleMock.connect = jest.fn().mockImplementation(() => {
console.log('Connected to mock DB');
});
}
return moduleMock;
});

Documentación

Otras mejoras

Serialización personalizada de objetos

Las utilidades de comparación de Jest ahora permiten definir una propiedad estática SERIALIZABLE_PROPERTIES en objetos personalizados. Esto te permite controlar qué propiedades se incluyen en snapshots y mensajes de error, haciendo la salida más enfocada y relevante.

Documentación

Soporte para configuración asíncrona

Los archivos de prueba listados en setupFilesAfterEnv ahora pueden exportar funciones asíncronas o usar await de nivel superior, similar a setupFiles.

Y mucho más…

Consulta el CHANGELOG completo para ver todos los cambios, mejoras y nuevas características.

Problemas conocidos

jsdom ha implementado cambios para cumplir mejor con las especificaciones. Esto podría romper algunos casos de uso, especialmente al simular window.location en pruebas. Jest ahora incluye @jest/environment-jsdom-abstract para facilitar la creación de entornos de prueba personalizados basados en jsdom. Si solo necesitas parchear jsdom, puedes aplicar este parche de jsdom en tu proyecto. En el futuro, exploraremos alternativas a jsdom más adecuadas para pruebas.

¿Qué viene después?

Jest ha sido el framework de pruebas JavaScript más popular durante una década. Lo usan millones de desarrolladores en proyectos que van desde pequeñas librerías hasta los mayores codebases del mundo. Con el tiempo hemos acumulado deuda técnica: mantenemos funciones poco usadas y minimizamos cambios importantes para no afectar usuarios. Algunas características deberían existir fuera del core, otras fomentan malas prácticas. Además, cambios en el equipo han ralentizado nuestro progreso. Así abordaremos estos desafíos:

  • Rendimiento/Deuda técnica: Reducir Jest a un núcleo más ligero y eficiente. Eliminar funciones minoritarias y enfocarnos en lo que hace grande a Jest.

  • Ciclos de lanzamiento consistentes: Seremos más regulares con versiones y políticas de desuso.

  • Transparencia: Construir todo abiertamente, compartiendo nuestros planes. Ofrecer más oportunidades para colaborar y aumentar contribuyentes.

  • Sé Audaz: Como equipo de Jest, debemos ser más atrevidos. Hay muchas cosas que impiden que Jest alcance todo su potencial. Es hora de tomar acción.

La buena noticia es que Jest siempre ha estado bien preparado para cumplir con estos principios, desde que construimos el framework como un sistema modular con clara separación de responsabilidades. Ahora es momento de ejecutar. ¡Más sobre esto próximamente!

Agradecimientos

Esta versión no habría sido posible sin el arduo trabajo de nuestra comunidad. Gracias.

@SimenB, @mrazauskas, @Connormiha, @liuxingbaoyu, @k-rajat19, @G-Rath, @charpeni, @dubzzz, @stekycz, @yinm, @lencioni, @phawxby, @lukeapage, @robhogan, @fisker, @k-rajat19, @connectdotz, @alesmenzel, @rickhanlonii, @mbelsky, @brunocabral88, @brandon-leapyear, @nicolo-ribaudo, @dj-stormtrooper, @eryue0220

Un agradecimiento especial a todos quienes hicieron su primera contribución a Jest en esta versión. ¡Gracias por hacer que Jest sea mejor para todos!

@eyalroth, @KhaledElmorsy, @mohammednumaan, @bensternthal, @BondarenkoAlex, @phryneas, @jayvdb, @brandonchinn178, @latin-1, @rmartine-ias, @fa93hws, @Dunqing, @gustav0d, @noritaka1166, @andreibereczki, @Dreamsorcerer, @satanTime, @icholy, @ecraig12345, @cgm-16, @sebastiancarlos, @dancer1325, @loganrosen, @zakingslayerv22, @dev-intj, @tez3998, @anbnyc, @pengqiseven, @thypon, @co63oc, @danielrentz, @jonasongg, @andrew-the-drawer, @phryneas, @hyperupcall, @tonyd33, @madcapnmckay, @dongwa, @gagan-bhullar-tech, @ikonst, @ZuBB, @jzaefferer, @brandonnorsworthy, @henny1105, @DmitryMakhnev, @askoufis, @RahulARanger, @Jon-Biz, @fynsta, @KonnorRogers, @BondarenkoAlex, @mouadhbb, @kemuridama, @Avi-E-Koenig, @davidroeca, @akwodkiewicz, @mukul-turing, @dnicolson, @colinacassidy, @ofekm97, @haze, @Vadimchesh, @peterdenham, @ShuZhong, @manoraj, @nicolo-ribaudo, @georgekaran, @MathieuFedrigo, @hkdobrev, @Germandrummer92, @CheadleCheadle, @notaphplover, @danbeam, @arescrimson, @yepitschunked, @JimminiKin, @DerTimonius, @vkml, @ginabethrussell, @jeremiah-snee-openx, @WillianAgostini, @casey-lentz, @faizanu94, @someone635, @rafaelrabelos, @RayBrokeSomething, @DaniAcu, @mattkubej, @tr1ckydev, @shresthasurav, @the-ress, @Mutesa-Cedric, @nolddor, @alexreardon, @Peeja, @verycosy, @mknight-atl, @maro1993, @Eric-Tyrrell22