Guía de migración
Migración a Vitest 3.0
Opciones de Prueba como Tercer Argumento
Vitest 3.0 emitirá una advertencia si se pasa un objeto como tercer argumento a las funciones test
o describe
:
test('validation works', () => {
// ...
}, { retry: 3 });
test('validation works', { retry: 3 }, () => {
// ...
});
La próxima versión principal lanzará un error si el tercer argumento es un objeto. Sin embargo, el valor del tiempo de espera sigue siendo válido:
test('validation works', () => {
// ...
}, 1000); // Ok ✅
browser.name
y browser.providerOptions
están Obsoletos
Tanto browser.name
como browser.providerOptions
serán eliminados en Vitest 4. En su lugar, utiliza la nueva opción browser.instances
:
export default defineConfig({
test: {
browser: {
name: 'chromium',
providerOptions: {
launch: { devtools: true },
},
instances: [
{
browser: 'chromium',
launch: { devtools: true },
},
],
},
},
})
Con el nuevo campo browser.instances
, también puedes especificar múltiples configuraciones de navegador.
spy.mockReset
Ahora Restaura la Implementación Original
Anteriormente, no existía una forma adecuada de restablecer un espía a su implementación original sin tener que reconfigurarlo. Ahora, spy.mockReset
restablecerá la implementación del mock a la original en lugar de a una función no-op simulada.
const foo = {
bar: () => 'Hello, world!',
};
vi.spyOn(foo, 'bar').mockImplementation(() => 'Hello, mock!');
foo.bar(); // 'Hello, mock!'
foo.bar.mockReset();
foo.bar(); // undefined
foo.bar(); // 'Hello, world!'
vi.spyOn
Reutiliza el Mock si el Método ya está Simulado
Anteriormente, Vitest siempre asignaba un nuevo espía al espiar un objeto. Esto causaba errores con mockRestore
, ya que restauraba el espía anterior en lugar de la función original:
vi.spyOn(fooService, 'foo').mockImplementation(() => 'bar');
vi.spyOn(fooService, 'foo').mockImplementation(() => 'bar');
vi.restoreAllMocks();
vi.isMockFunction(fooService.foo); // true
vi.isMockFunction(fooService.foo); // false
Valores Predeterminados de Temporizadores Simulados
Vitest ya no incluye opciones predeterminadas para fakeTimers.toFake
. Ahora, Vitest simulará cualquier API relacionada con temporizadores si está disponible (excepto nextTick
). En particular, performance.now()
ahora se simula cuando se llama a vi.useFakeTimers
.
vi.useFakeTimers();
performance.now(); // original
performance.now(); // fake
Puedes revertir al comportamiento anterior especificando explícitamente los temporizadores al llamar a vi.useFakeTimers
o configurándolos globalmente:
export default defineConfig({
test: {
fakeTimers: {
toFake: [
'setTimeout',
'clearTimeout',
'setInterval',
'clearInterval',
'setImmediate',
'clearImmediate',
'Date',
]
},
},
})
Igualdad de Errores Más Estricta
Vitest ahora verifica más propiedades al comparar errores usando toEqual
o toThrowError
. Vitest compara name
, message
, cause
y AggregateError.errors
. Para Error.cause
, la comparación es asimétrica:
expect(new Error('hi', { cause: 'x' })).toEqual(new Error('hi')); // ✅
expect(new Error('hi')).toEqual(new Error('hi', { cause: 'x' })); // ❌
Además de verificar más propiedades, Vitest ahora compara los prototipos de los errores. Por ejemplo, si se lanzó un TypeError
, la verificación de igualdad debe ser de tipo TypeError
, no Error
:
expect(() => {
throw new TypeError('type error');
})
.toThrowError(new Error('type error'))
.toThrowError(new TypeError('type error'));
Para más detalles, consulta el PR: #5876.
La Exportación Condicional module
no se Resuelve por Defecto en Vite 6
Vite 6 permite opciones resolve.conditions
más flexibles, y Vitest las configura para excluir la exportación condicional module
por defecto. Consulta también la guía de migración de Vite 6 para obtener detalles sobre los cambios del lado de Vite.
El Tipo Custom
está Obsoleto API
El tipo Custom
ahora es un alias para el tipo Test
. Ten en cuenta que Vitest actualizó los tipos públicos en 2.1 y cambió los nombres exportados a RunnerCustomCase
y RunnerTestCase
:
import {
RunnerCustomCase,
RunnerTestCase,
} from 'vitest';
Si estás usando getCurrentSuite().custom()
, el type
de la tarea devuelta ahora será 'test'
. El tipo Custom
se eliminará en Vitest 4.
El Tipo WorkspaceSpec
Ya No se Utiliza API
En la API pública, este tipo se utilizaba anteriormente en secuenciadores personalizados. Por favor, migra a TestSpecification
en su lugar.
onTestFinished
y onTestFailed
Ahora Reciben un Contexto
Los hooks onTestFinished
y onTestFailed
anteriormente recibían el resultado de la prueba como primer argumento. Ahora, al igual que beforeEach
y afterEach
, reciben un contexto de prueba.
Cambios en la API de Snapshots API
La API pública de Snapshots en @vitest/snapshot
se modificó para admitir múltiples estados en una única ejecución. Consulta el PR para más detalles: #6817
Cabe destacar que estos cambios solo afectan a los desarrolladores que usan la API de Snapshots directamente. No hubo cambios en la API de .toMatchSnapshot
.
Cambios en la Firma del Tipo resolveConfig
API
resolveConfig
ahora ofrece mayor utilidad. En lugar de aceptar una configuración de Vite ya resuelta, ahora acepta una configuración de usuario y devuelve la configuración resuelta.
Esta función no se utiliza internamente y se expone únicamente como una API pública.
Tipos de vitest/reporters
Simplificados API
El punto de entrada vitest/reporters
ahora exporta únicamente las implementaciones de los reporteros y sus tipos de opciones. Si necesitas acceso a TestCase
/TestSuite
y otros tipos relacionados con tareas, impórtalos también desde vitest/node
.
La Cobertura Ignora los Archivos de Prueba Incluso Cuando coverage.excludes
se Anula
Ya no es posible incluir archivos de prueba en el informe de cobertura anulando coverage.excludes
. Los archivos de prueba ahora siempre se excluyen.
Migración a Vitest 2.0
El Pool Predeterminado es forks
Vitest 2.0 establece la configuración predeterminada de pool
a 'forks'
para una mejor estabilidad. Puedes leer la motivación completa en el PR.
Si has utilizado poolOptions
sin haber especificado un pool
, es posible que necesites actualizar la configuración:
export default defineConfig({
test: {
poolOptions: {
threads: {
singleThread: true,
},
forks: {
singleFork: true,
},
}
}
})
Los Hooks se Ejecutan en Secuencia
Antes de Vitest 2.0, todos los hooks se ejecutaban en paralelo. A partir de la versión 2.0, se ejecutan secuencialmente. Además, los hooks afterAll
/afterEach
se ejecutan en orden inverso.
Para revertir a la ejecución paralela de los hooks, cambia sequence.hooks
a 'parallel'
:
export default defineConfig({
test: {
sequence: {
hooks: 'parallel',
},
},
})
suite.concurrent
Ejecuta Todas las Pruebas Concurrentemente
Anteriormente, especificar concurrent
en una suite agrupaba las pruebas concurrentes dentro de cada suite, ejecutándolas secuencialmente. Ahora, siguiendo el comportamiento de Jest, todas las pruebas se ejecutan concurrentemente (respetando los límites de maxConcurrency
).
coverage.ignoreEmptyLines
para Cobertura V8 está Habilitado por Defecto
El valor predeterminado de coverage.ignoreEmptyLines
ahora es true
. Este cambio significativo puede afectar los informes de cobertura de código y podría requerir ajustes en los umbrales de cobertura para algunos proyectos. Este ajuste solo se aplica a la configuración predeterminada cuando coverage.provider
es 'v8'
.
Eliminación de la Opción watchExclude
Vitest usa el observador de Vite. Excluye archivos o directorios agregándolos a server.watch.ignored
:
export default defineConfig({
server: {
watch: {
ignored: ['!node_modules/examplejs']
}
}
})
--segfault-retry
Eliminado
Con los cambios en el pool por defecto, esta opción ya no es necesaria. Si experimentas errores de segfault, prueba a cambiar al pool 'forks'
. Si el problema persiste, abre un nuevo issue con una reproducción.
Eliminación de Tareas Vacías en Suites
Este es un cambio en la API de tareas avanzada. Anteriormente, recorrer .suite
eventualmente conducía a la suite interna vacía que sustituía a una tarea de archivo.
Esto hace que .suite
sea opcional: si la tarea se define en el nivel superior, no tendrá una suite asociada. Puedes usar la propiedad .file
, que ahora está presente en todas las tareas (incluida la propia tarea de archivo, así que ten cuidado de no caer en una recursión infinita).
Este cambio también elimina la referencia al archivo en expect.getState().currentTestName
y hace que expect.getState().testPath
sea obligatorio.
task.meta
se Incluye en el Reportero JSON
El reportero JSON ahora muestra task.meta
para cada resultado de aserción.
Tipos Genéricos Simplificados de Funciones Simuladas (ej. vi.fn<T>
, Mock<T>
)
Anteriormente, vi.fn<TArgs, TReturn>
aceptaba dos tipos genéricos de forma separada para los argumentos y el valor de retorno. Esto se cambió para aceptar directamente un tipo de función vi.fn<T>
para simplificar el uso.
import { vi } from 'vitest';
import type { Mock } from 'vitest';
const add = (x: number, y: number): number => x + y;
// using vi.fn<T>
const mockAdd = vi.fn<Parameters<typeof add>, ReturnType<typeof add>>();
const mockAdd = vi.fn<typeof add>();
// using Mock<T>
const mockAdd: Mock<Parameters<typeof add>, ReturnType<typeof add>> = vi.fn();
const mockAdd: Mock<typeof add> = vi.fn();
Acceso a mock.results
con Promesas Resueltas
Anteriormente, Vitest resolvía los valores de mock.results
si la función devolvía una Promesa. Ahora existe una propiedad mock.settledResults
separada que se completa únicamente cuando la Promesa devuelta se resuelve o se rechaza.
const fn = vi.fn().mockResolvedValueOnce('result');
await fn();
const result = fn.mock.results[0]; // 'result'
const result = fn.mock.results[0]; // 'Promise<result>'
const settledResult = fn.mock.settledResults[0]; // 'result'
Con este cambio, también introducimos nuevos matchers toHaveResolved*
, similares a toHaveReturned
, para facilitar la migración si previamente utilizabas este último:
const fn = vi.fn().mockResolvedValueOnce('result');
await fn();
expect(fn).toHaveReturned('result');
expect(fn).toHaveResolved('result');
Modo Navegador
El Modo Navegador de Vitest experimentó muchos cambios durante el ciclo beta. Puedes leer sobre nuestra filosofía en el Modo Navegador en la página de discusión de GitHub.
La mayoría de los cambios fueron aditivos, pero hubo algunos pequeños cambios disruptivos:
- El proveedor
none
fue renombrado apreview
#5842 - El proveedor
preview
ahora es el predeterminado #5842 indexScripts
fue renombrado aorchestratorScripts
#5842
Opciones Obsoletas Retiradas API
Se eliminaron algunas opciones obsoletas:
- Comando
vitest typecheck
- usavitest --typecheck
en su lugar - Variables de entorno
VITEST_JUNIT_CLASSNAME
yVITEST_JUNIT_SUITE_NAME
(usa las opciones del reportero en su lugar) - La verificación de cobertura con
c8
(usa coverage-v8 en su lugar) - Exportación de
SnapshotEnvironment
desdevitest
- impórtalo desdevitest/snapshot
en su lugar SpyInstance
se elimina a favor deMockInstance
Migración a Vitest 1.0
Requisitos Mínimos
Vitest 1.0 requiere Vite 5.0 y Node.js 18 o versiones posteriores.
Todos los subpaquetes @vitest/*
requieren Vitest versión 1.0.
Actualización de Snapshots #3961
Las comillas en los snapshots ya no se escapan, y todos los snapshots usan comillas graves (`) incluso para cadenas de una sola línea.
- Las comillas ya no se escapan:
expect({ foo: 'bar' }).toMatchInlineSnapshot(`
Object {
- \\"foo\\": \\"bar\\",
+ "foo": "bar",
}
`)
- Los snapshots de una línea ahora usan comillas "`" en lugar de ':
- expect('some string').toMatchInlineSnapshot('"some string"')
+ expect('some string').toMatchInlineSnapshot(`"some string"`)
También hubo cambios en el paquete @vitest/snapshot
. Si no lo estás usando directamente, no necesitas cambiar nada.
- Ya no necesitas extender
SnapshotClient
solo para sobrescribir el métodoequalityCheck
: simplemente pásalo comoisEqual
al iniciar una instancia client.setTest
fue renombrado aclient.startCurrentRun
client.resetCurrent
fue renombrado aclient.finishCurrentRun
Los Pools están Estandarizados #4172
Hemos eliminado muchas opciones de configuración para facilitar la adaptación del ejecutor a tus necesidades. Por favor, echa un vistazo a los ejemplos de migración si dependes de --threads
u otras banderas relacionadas.
--threads
ahora es--pool=threads
--no-threads
ahora es--pool=forks
--single-thread
ahora es--poolOptions.threads.singleThread
--experimental-vm-threads
ahora es--pool=vmThreads
--experimental-vm-worker-memory-limit
ahora es--poolOptions.vmThreads.memoryLimit
--isolate
ahora es--poolOptions.<pool-name>.isolate
ybrowser.isolate
test.maxThreads
ahora estest.poolOptions.<pool-name>.maxThreads
test.minThreads
ahora estest.poolOptions.<pool-name>.minThreads
test.useAtomics
ahora estest.poolOptions.<pool-name>.useAtomics
test.poolMatchGlobs.child_process
ahora estest.poolMatchGlobs.forks
test.poolMatchGlobs.experimentalVmThreads
ahora estest.poolMatchGlobs.vmThreads
{
scripts: {
- "test": "vitest --no-threads"
// For identical behaviour:
+ "test": "vitest --pool forks --poolOptions.forks.singleFork"
// Or multi parallel forks:
+ "test": "vitest --pool forks"
}
}
{
scripts: {
- "test": "vitest --experimental-vm-threads"
+ "test": "vitest --pool vmThreads"
}
}
{
scripts: {
- "test": "vitest --isolate false"
+ "test": "vitest --poolOptions.threads.isolate false"
}
}
{
scripts: {
- "test": "vitest --no-threads --isolate false"
+ "test": "vitest --pool forks --poolOptions.forks.isolate false"
}
}
Cambios en la Cobertura #4265, #4442
La opción coverage.all
ahora está habilitada de forma predeterminada. Esto significa que todos los archivos del proyecto que coincidan con el patrón coverage.include
se procesarán aunque no se ejecuten.
La estructura de la API de umbrales de cobertura ha cambiado, y ahora permite especificar umbrales para archivos específicos utilizando patrones glob:
export default defineConfig({
test: {
coverage: {
- perFile: true,
- thresholdAutoUpdate: true,
- 100: true,
- lines: 100,
- functions: 100,
- branches: 100,
- statements: 100,
+ thresholds: {
+ perFile: true,
+ autoUpdate: true,
+ 100: true,
+ lines: 100,
+ functions: 100,
+ branches: 100,
+ statements: 100,
+ }
}
}
})
Tipos de Mock #4400
Se eliminaron algunos tipos a favor de la nomenclatura "Mock" al estilo Jest.
- import { EnhancedSpy, SpyInstance } from 'vitest'
+ import { MockInstance } from 'vitest'
WARNING
SpyInstance
está obsoleto a favor de MockInstance
y se eliminará en la próxima versión principal.
Mocks de Temporizadores #3925
vi.useFakeTimers()
ya no simula de forma automática process.nextTick
. Todavía es posible simular process.nextTick
especificándolo explícitamente al usar vi.useFakeTimers({ toFake: ['nextTick'] })
.
Sin embargo, la simulación de process.nextTick
no es posible al usar --pool=forks
. Usa una opción --pool
diferente si necesitas simular process.nextTick
.
Migración desde Jest
Vitest ha sido diseñado con una API compatible con Jest, para hacer la migración desde Jest lo más simple posible. A pesar de esos esfuerzos, aún puedes observar las siguientes diferencias:
Globales por Defecto
Jest tiene su API global habilitada por defecto, mientras que Vitest no. Puedes habilitar las globales mediante la configuración globals
o actualizar tu código para usar importaciones del módulo vitest
.
Si decides mantener las globales deshabilitadas, es importante considerar que las bibliotecas comunes como testing-library
no realizarán la limpieza automática del DOM.
spy.mockReset
mockReset
de Jest reemplaza la implementación del mock con una función vacía que devuelve undefined
.
mockReset
de Vitest restablece la implementación del mock a su original. Es decir, restablecer un mock creado por vi.fn(impl)
restablecerá la implementación del mock a impl
.
Mocks de Módulos
Al simular un módulo en Jest, el argumento de fábrica devuelve la exportación predeterminada. En Vitest, el argumento de fábrica debe devolver un objeto con cada exportación explícitamente definida. Por ejemplo, el siguiente jest.mock
tendría que actualizarse de la siguiente manera:
jest.mock('./some-path', () => 'hello')
vi.mock('./some-path', () => ({
default: 'hello',
}))
Para más detalles, consulta la sección de la API vi.mock
.
Comportamiento de Auto-Simulación
A diferencia de Jest, los módulos simulados en <root>/__mocks__
no se cargan si no se llama a vi.mock()
. Si necesitas que se simulen en cada prueba, como en Jest, puedes configurarlos dentro de setupFiles
.
Importar el Original de un Paquete Simulado
Si solo estás creando mocks parciales para un paquete, es posible que hayas usado anteriormente la función requireActual
de Jest. En Vitest, debes sustituir estas llamadas por vi.importActual
.
const { cloneDeep } = jest.requireActual('lodash/cloneDeep');
const { cloneDeep } = await vi.importActual('lodash/cloneDeep');
Extender la Simulación a Librerías Externas
A diferencia de Jest, que extiende la simulación por defecto, cuando simulas un módulo y deseas que esta simulación se aplique también a otras librerías externas que utilizan el mismo módulo, debes especificar explícitamente qué librería de terceros quieres simular. Esto se logra haciendo que la librería externa se considere parte de tu código fuente, utilizando server.deps.inline.
server.deps.inline: ["lib-name"]
expect.getState().currentTestName
Los nombres de test
de Vitest usan el símbolo >
como separador para facilitar la distinción entre pruebas y suites, mientras que Jest usa un espacio vacío ().
- `${describeTitle} ${testTitle}`
+ `${describeTitle} > ${testTitle}`
Variables de Entorno
Al igual que Jest, Vitest establece NODE_ENV
a test
, si no se había establecido antes. Vitest también tiene un equivalente para JEST_WORKER_ID
llamado VITEST_POOL_ID
(siempre menor o igual a maxThreads
), por lo que si dependes de ella, no olvides renombrarla. Vitest también expone VITEST_WORKER_ID
, que es un ID único de un worker en ejecución. Este número no se ve afectado por maxThreads
y aumentará con cada worker creado.
Sustituir Propiedad
En Jest, para modificar un objeto, se utiliza la API replaceProperty. En Vitest, puedes lograr el mismo efecto usando vi.stubEnv
o vi.spyOn
.
Callback Done
A partir de Vitest v0.10.0, el uso de callbacks para declarar pruebas está obsoleto. Puedes reescribirlas para usar funciones async
/await
, o usar Promesas para simular el comportamiento de los callbacks.
it('should work', (done) => {
it('should work', () => new Promise(done => {
// ...
done()
})
}))
Hooks
Los hooks beforeAll
/beforeEach
pueden devolver una función de desmontaje en Vitest. Debido a esto, es posible que necesites reescribir tus declaraciones de hooks si devuelven algo distinto de undefined
o null
:
beforeEach(() => setActivePinia(createTestingPinia()))
beforeEach(() => { setActivePinia(createTestingPinia()) })
En Jest, los hooks se llaman secuencialmente. Por defecto, Vitest ejecuta los hooks en paralelo. Para usar el comportamiento de Jest, actualiza la opción sequence.hooks
:
export default defineConfig({
test: {
sequence: {
hooks: 'list',
}
}
})
Tipos
Vitest no tiene un equivalente al espacio de nombres jest
, por lo que necesitarás importar los tipos directamente desde vitest
:
let fn: jest.Mock<(name: string) => number>;
import type { Mock } from 'vitest';
let fn: Mock<(name: string) => number>;
Temporizadores
Vitest no soporta los temporizadores heredados de Jest.
Tiempo de Espera
Si usaste jest.setTimeout
, necesitarías migrar a vi.setConfig
:
jest.setTimeout(5_000);
vi.setConfig({ testTimeout: 5_000 });
Snapshots de Vue
Aunque no es una característica específica de Jest, si anteriormente utilizabas Jest con el preajuste de vue-cli, necesitarás instalar el paquete jest-serializer-vue
y configurarlo dentro de setupFiles
:
import { defineConfig } from 'vite';
export default defineConfig({
test: {
setupFiles: ['./tests/unit/setup.js'],
},
});
import vueSnapshotSerializer from 'jest-serializer-vue';
expect.addSnapshotSerializer(vueSnapshotSerializer);
De lo contrario, tus snapshots tendrán muchas comillas escapadas.