Vi
Vitest proporciona funciones útiles a través de su helper vi
. Puedes acceder a él globalmente (cuando la configuración de globales está habilitada) o importarlo directamente desde vitest
:
import { vi } from 'vitest';
Módulos Simulados
Esta sección describe la API que puedes usar al simular un módulo. Ten en cuenta que Vitest no soporta simular módulos importados con require()
.
vi.mock
- Tipo:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Tipo:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Sustituye todos los módulos importados desde la ruta
proporcionada por otro módulo. Puedes usar alias de Vite configurados dentro de una ruta. La llamada a vi.mock
se eleva (hoisted), por lo que no importa dónde la llames. Siempre se ejecuta antes que cualquier importación. Si necesitas hacer referencia a algunas variables fuera de su alcance, puedes definirlas dentro de vi.hoisted
y hacer referencia a ellas dentro de vi.mock
.
WARNING
vi.mock
solo funciona para módulos que fueron importados con la palabra clave import
. No funciona con require
.
Para elevar vi.mock
, Vitest analiza estáticamente tus archivos. Esto significa que no se puede usar vi
que no haya sido importado directamente del paquete vitest
(por ejemplo, desde algún archivo de utilidad). Usa vi.mock
con vi
importado de vitest
, o habilita la opción de configuración globals
.
Vitest no simulará módulos que fueron importados dentro de un archivo setup porque ya están en caché cuando se ejecuta un archivo de prueba. Puedes llamar a vi.resetModules()
dentro de vi.hoisted
para limpiar la caché de todos los módulos antes de ejecutar un archivo de prueba.
Si la función factory
está definida, todas las importaciones devolverán su resultado. Vitest llama a la factory solo una vez y almacena en caché los resultados para todas las importaciones posteriores hasta que se llama a vi.unmock
o vi.doUnmock
.
A diferencia de jest
, la factory puede ser asíncrona. Puedes usar vi.importActual
o un helper con la factory pasada como primer argumento, y obtener el módulo original dentro.
Desde Vitest 2.1, también puedes proporcionar un objeto con una propiedad spy
en lugar de una función factory. Si spy
es true
, entonces Vitest auto-simulará el módulo como de costumbre, pero no sobrescribirá la implementación de las exportaciones. Esto es útil si solo quieres verificar que el método exportado fue llamado correctamente por otro método.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
// llama a la implementación original,
// pero permite verificar el comportamiento más tarde
const result = calculator(1, 2);
expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
Vitest también admite una promesa de módulo en lugar de una cadena en los métodos vi.mock
y vi.doMock
para un mejor soporte de IDE. Cuando se mueve el archivo, la ruta se actualizará, y importOriginal
hereda automáticamente el tipo. Usar esta firma también garantizará que el tipo de retorno de la factory sea compatible con el módulo original (manteniendo las exportaciones opcionales).
// @filename: ./path/to/module.js
export declare function total(...numbers: number[]): number;
// @filename: test.js
import { vi } from 'vitest';
// ---cut---
vi.mock(import('./path/to/module.js'), async importOriginal => {
const mod = await importOriginal(); // el tipo se infiere
// ^?
return {
...mod,
// reemplazar algunas exportaciones
total: vi.fn(),
};
});
Internamente, Vitest sigue operando con una cadena y no con un objeto de módulo.
Sin embargo, si estás usando TypeScript con alias paths
configurados en tsconfig.json
, el compilador no podrá resolver correctamente los tipos de importación. Para que funcione, asegúrate de reemplazar todas las importaciones con alias por sus rutas relativas correspondientes. Por ejemplo, usa import('./path/to/module.js')
en lugar de import('@/module')
.
WARNING
vi.mock
se eleva (en otras palabras, se mueve) a la parte superior del archivo. Esto significa que, independientemente de dónde lo escribas (ya sea dentro de beforeEach
o test
), realmente se ejecutará antes.
Esto también significa que no puedes usar ninguna variable definida fuera de la factory dentro de la factory.
Si necesitas usar variables dentro de la factory, prueba vi.doMock
. Funciona de la misma manera pero no se eleva. Ten en cuenta que solo simula las importaciones subsiguientes.
También puedes hacer referencia a variables definidas por el método vi.hoisted
si se declaró antes de vi.mock
:
import { namedExport } from './path/to/module.js';
const mocks = vi.hoisted(() => {
return {
namedExport: vi.fn(),
};
});
vi.mock('./path/to/module.js', () => {
return {
namedExport: mocks.namedExport,
};
});
vi.mocked(namedExport).mockReturnValue(100);
expect(namedExport()).toBe(100);
expect(namedExport).toBe(mocks.namedExport);
WARNING
Si estás simulando un módulo con una exportación por defecto, deberás proporcionar una clave default
dentro del objeto devuelto por la función factory. Esta es una particularidad específica de los módulos ES; por lo tanto, la documentación de jest
puede diferir ya que jest
utiliza módulos CommonJS. Por ejemplo,
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// etc...
};
});
Si hay una carpeta __mocks__
junto a un archivo que estás simulando, y la factory no se proporciona, Vitest intentará encontrar un archivo con el mismo nombre en la subcarpeta __mocks__
y usarlo como un módulo real. Si estás simulando una dependencia, Vitest intentará encontrar una carpeta __mocks__
en la raíz del proyecto (por defecto es process.cwd()
). Puedes indicarle a Vitest dónde se encuentran las dependencias a través de la opción de configuración deps.moduleDirectories
.
Por ejemplo, tienes esta estructura de archivos:
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
Si llamas a vi.mock
en un archivo de prueba sin proporcionar una factory u opciones, buscará un archivo en la carpeta __mocks__
para usarlo como módulo:
// increment.test.js
import { vi } from 'vitest';
// axios es una exportación por defecto de `__mocks__/axios.js`
import axios from 'axios';
// increment es una exportación nombrada de `src/__mocks__/increment.js`
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
Ten en cuenta que si no llamas a vi.mock
, los módulos no se simulan automáticamente. Para replicar el comportamiento de simulación automática de Jest, puedes llamar a vi.mock
para cada módulo requerido dentro de setupFiles
.
Si no hay una carpeta __mocks__
o una factory proporcionada, Vitest importará el módulo original y auto-simulará todas sus exportaciones. Para conocer las reglas aplicadas, consulta algoritmo.
vi.doMock
- Tipo:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Tipo:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Igual que vi.mock
, pero no se eleva a la parte superior del archivo, por lo que puedes hacer referencia a variables en el ámbito global del archivo. La siguiente importación dinámica del módulo se simulará.
WARNING
Esto no simulará módulos que fueron importados antes de que se llamara a este método. No olvides que todas las importaciones estáticas en ESM siempre se elevan, por lo que poner esto antes de una importación estática no forzará que se llame antes de la importación:
vi.doMock('./increment.js'); // esto se llamará _después_ de la declaración de importación
import { increment } from './increment.js';
// ./increment.js
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// el módulo no está simulado, porque vi.doMock aún no se ha llamado
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// puedes acceder a variables dentro de una factory
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('importar el siguiente módulo importa el simulado', async () => {
// la importación original NO FUE SIMULADA, porque vi.doMock se evalúa DESPUÉS de las importaciones
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// la nueva importación dinámica devuelve el módulo simulado
expect(mockedIncrement(1)).toBe(101);
expect(mockedIncrement(1)).toBe(102);
expect(mockedIncrement(1)).toBe(103);
});
vi.mocked
- Tipo:
<T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
- Tipo:
<T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>
Ayuda de tipo para TypeScript. Simplemente devuelve el objeto que se pasó.
Cuando partial
es true
, esperará un Partial<T>
como valor de retorno. Por defecto, esto solo hará que TypeScript crea que los valores de primer nivel están simulados. Puedes pasar { deep: true }
como segundo argumento para indicarle a TypeScript que todo el objeto está simulado, si realmente lo está.
// example.ts
export function add(x: number, y: number): number {
return x + y;
}
export function fetchSomething(): Promise<Response> {
return fetch('https://vitest.dev/');
}
// example.test.ts
import * as example from './example';
vi.mock('./example');
test('1 + 1 es igual a 10', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('simular valor de retorno con tipado solo parcialmente correcto', async () => {
vi.mocked(example.fetchSomething).mockResolvedValue(new Response('hello'));
vi.mocked(example.fetchSomething, { partial: true }).mockResolvedValue({
ok: false,
});
// vi.mocked(example.someFn).mockResolvedValue({ ok: false }) // esto es un error de tipo
});
vi.importActual
- Tipo:
<T>(path: string) => Promise<T>
Importa un módulo, omitiendo todas las comprobaciones sobre si debe ser simulado. Puede ser útil si quieres simular un módulo parcialmente.
vi.mock('./example.js', async () => {
const axios = await vi.importActual('./example.js');
return { ...axios, get: vi.fn() };
});
vi.importMock
- Tipo:
<T>(path: string) => Promise<MaybeMockedDeep<T>>
Importa un módulo con todas sus propiedades (incluidas las propiedades anidadas) simuladas. Sigue las mismas reglas que vi.mock
. Para conocer las reglas aplicadas, consulta algoritmo.
vi.unmock
- Tipo:
(path: string | Promise<Module>) => void
Elimina un módulo del registro de simulaciones. Todas las llamadas para importar devolverán el módulo original, incluso si fue simulado antes. Esta llamada se eleva a la parte superior del archivo, por lo que solo deshará la simulación de módulos que se definieron en setupFiles
, por ejemplo.
vi.doUnmock
- Tipo:
(path: string | Promise<Module>) => void
Igual que vi.unmock
, pero no se eleva a la parte superior del archivo. La siguiente importación del módulo importará el módulo original en lugar del simulado. Esto no deshará la simulación de módulos importados previamente.
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment ya está simulado, porque vi.mock se eleva
increment(1) === 100;
// esto se eleva, y la factory se llama antes de la importación en la línea 1
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// todas las llamadas están simuladas, y `increment` siempre devuelve 100
increment(1) === 100;
increment(30) === 100;
// esto no se eleva, por lo que otra importación devolverá el módulo sin simular
vi.doUnmock('./increment.js');
// esto TODAVÍA devuelve 100, porque `vi.doUnmock` no reevalúa un módulo
increment(1) === 100;
increment(30) === 100;
// la siguiente importación no está simulada, ahora `increment` es la función original que devuelve count + 1
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- Tipo:
() => Vitest
Restablece el registro de módulos limpiando la caché de todos los módulos. Esto permite que los módulos se reevalúen al reimportarlos. Las importaciones de nivel superior no se pueden reevaluar. Puede ser útil para aislar módulos donde el estado local entra en conflicto entre pruebas.
import { vi } from 'vitest';
import { data } from './data.js'; // No se reevaluará antes de cada prueba
beforeEach(() => {
vi.resetModules();
});
test('cambiar estado', async () => {
const mod = await import('./some/path.js'); // Se reevaluará
mod.changeLocalState('new value');
expect(mod.getLocalState()).toBe('new value');
});
test('el módulo tiene estado antiguo', async () => {
const mod = await import('./some/path.js'); // Se reevaluará
expect(mod.getLocalState()).toBe('old value');
});
WARNING
No restablece el registro de simulaciones. Para limpiar el registro de simulaciones, usa vi.unmock
o vi.doUnmock
.
vi.dynamicImportSettled
Espera a que se carguen todas las importaciones. Útil si tienes una llamada síncrona que inicia la importación de un módulo que de otra manera no podrías esperar.
import { expect, test } from 'vitest';
// no se puede rastrear la importación porque no se devuelve una Promesa
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('las operaciones se resuelven', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
Si durante una importación dinámica se inicia otra importación dinámica, este método esperará hasta que todas se resuelvan.
Este método también esperará al siguiente tick de setTimeout
después de que se resuelva la importación para que todas las operaciones síncronas se completen cuando se resuelva.
Simulación de Funciones y Objetos
Esta sección describe cómo trabajar con simulaciones de métodos y reemplazar variables de entorno y globales.
vi.fn
- Tipo:
(fn?: Function) => Mock
Crea un espía en una función, aunque se puede iniciar sin una. Cada vez que se invoca una función, almacena sus argumentos de llamada, retornos e instancias. Además, puedes manipular su comportamiento con métodos. Si no se proporciona ninguna función, la simulación devolverá undefined
cuando se invoque.
const getApples = vi.fn(() => 0);
getApples();
expect(getApples).toHaveBeenCalled();
expect(getApples).toHaveReturnedWith(0);
getApples.mockReturnValueOnce(5);
const res = getApples();
expect(res).toBe(5);
expect(getApples).toHaveNthReturnedWith(2, 5);
vi.isMockFunction
- Tipo:
(fn: Function) => boolean
Comprueba que un parámetro dado es una función simulada. Si estás usando TypeScript, también restringirá su tipo.
vi.clearAllMocks
Llamará a .mockClear()
en todos los espías. Esto limpiará el historial de simulación, pero no restablecerá su implementación a la predeterminada.
vi.resetAllMocks
Llamará a .mockReset()
en todos los espías. Esto limpiará el historial de simulación y restablecerá su implementación a una función vacía (devolverá undefined
).
vi.restoreAllMocks
Llamará a .mockRestore()
en todos los espías. Esto limpiará el historial de simulación y restablecerá su implementación a la original.
vi.spyOn
- Tipo:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Crea un espía en un método o getter/setter de un objeto similar a vi.fn()
. Devuelve una función simulada.
let apples = 0;
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockImplementation(() => apples);
apples = 1;
expect(cart.getApples()).toBe(1);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveReturnedWith(1);
TIP
Puedes llamar a vi.restoreAllMocks
dentro de afterEach
(o habilitar test.restoreMocks
) para restaurar todos los métodos a sus implementaciones originales. Esto restaurará el descriptor de objeto original, por lo que no podrás cambiar la implementación del método:
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockReturnValue(10);
console.log(cart.getApples()); // 10
vi.restoreAllMocks();
console.log(cart.getApples()); // 42
spy.mockReturnValue(10);
console.log(cart.getApples()); // ¡todavía 42!
TIP
No es posible espiar métodos exportados en Modo Navegador. En su lugar, puedes espiar cada método exportado llamando a vi.mock("./file-path.js", { spy: true })
. Esto simulará cada exportación pero mantendrá su implementación intacta, permitiéndote afirmar si el método fue llamado correctamente.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
calculator(1, 2);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
Y aunque es posible espiar exportaciones en jsdom
u otros entornos Node.js, esto podría cambiar en el futuro.
vi.stubEnv
- Tipo:
<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
Cambia el valor de una variable de entorno en process.env
e import.meta.env
. Puedes restaurar su valor llamando a vi.unstubAllEnvs
.
import { vi } from 'vitest';
// `process.env.NODE_ENV` e `import.meta.env.NODE_ENV`
// son "development" antes de llamar a "vi.stubEnv"
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
vi.stubEnv('NODE_ENV', undefined);
process.env.NODE_ENV === undefined;
import.meta.env.NODE_ENV === undefined;
// no cambia otras variables de entorno
import.meta.env.MODE === 'development';
TIP
También puedes cambiar el valor simplemente asignándolo, pero no podrás usar vi.unstubAllEnvs
para restaurar el valor anterior:
import.meta.env.MODE = 'test';
vi.unstubAllEnvs
- Tipo:
() => Vitest
Restaura todos los valores de import.meta.env
y process.env
que fueron cambiados con vi.stubEnv
. Cuando se llama por primera vez, Vitest recuerda el valor original y lo almacenará hasta que se vuelva a llamar a unstubAllEnvs
.
import { vi } from 'vitest';
// `process.env.NODE_ENV` e `import.meta.env.NODE_ENV`
// son "development" antes de llamar a stubEnv
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
vi.stubEnv('NODE_ENV', 'staging');
process.env.NODE_ENV === 'staging';
import.meta.env.NODE_ENV === 'staging';
vi.unstubAllEnvs();
// restaura al valor que se almacenó antes de la primera llamada a "stubEnv"
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- Tipo:
(name: string | number | symbol, value: unknown) => Vitest
Cambia el valor de una variable global. Puedes restaurar su valor original llamando a vi.unstubAllGlobals
.
import { vi } from 'vitest';
// `innerWidth` es "0" antes de llamar a stubGlobal
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// si estás usando jsdom o happy-dom
window.innerWidth === 100;
TIP
También puedes cambiar el valor simplemente asignándolo a globalThis
o window
(si estás usando el entorno jsdom
o happy-dom
), pero no podrás usar vi.unstubAllGlobals
para restaurar el valor original:
globalThis.innerWidth = 100;
// si estás usando jsdom o happy-dom
window.innerWidth = 100;
vi.unstubAllGlobals
- Tipo:
() => Vitest
Restaura todos los valores globales en globalThis
/global
(y window
/top
/self
/parent
, si estás usando el entorno jsdom
o happy-dom
) que fueron cambiados con vi.stubGlobal
. Cuando se llama por primera vez, Vitest recuerda el valor original y lo almacenará hasta que se vuelva a llamar a unstubAllGlobals
.
import { vi } from 'vitest';
const Mock = vi.fn();
// IntersectionObserver es "undefined" antes de llamar a "stubGlobal"
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// si estás usando jsdom o happy-dom
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// lanza ReferenceError, porque no está definido
IntersectionObserver === undefined;
Temporizadores Falsos
Esta sección describe cómo trabajar con temporizadores falsos.
vi.advanceTimersByTime
- Tipo:
(ms: number) => Vitest
Este método invocará cada temporizador iniciado hasta que pase el número especificado de milisegundos o la cola esté vacía, lo que ocurra primero.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- Tipo:
(ms: number) => Promise<Vitest>
Este método invocará cada temporizador iniciado hasta que pase el número especificado de milisegundos o la cola esté vacía, lo que ocurra primero. Esto incluirá temporizadores establecidos de forma asíncrona.
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersByTimeAsync(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
- Tipo:
() => Vitest
Llamará al siguiente temporizador disponible. Útil para hacer afirmaciones entre cada llamada al temporizador. Puedes encadenar llamadas para gestionar los temporizadores tú mismo.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Tipo:
() => Promise<Vitest>
Llamará al siguiente temporizador disponible y esperará hasta que se resuelva si se estableció de forma asíncrona. Útil para hacer afirmaciones entre cada llamada al temporizador.
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersToNextTimerAsync(); // log: 1
expect(console.log).toHaveBeenCalledWith(1);
await vi.advanceTimersToNextTimerAsync(); // log: 2
await vi.advanceTimersToNextTimerAsync(); // log: 3
vi.advanceTimersToNextFrame 2.1.0+
- Tipo:
() => Vitest
Similar a vi.advanceTimersByTime
, pero avanzará los temporizadores por los milisegundos necesarios para ejecutar las devoluciones de llamada actualmente programadas con requestAnimationFrame
.
let frameRendered = false;
requestAnimationFrame(() => {
frameRendered = true;
});
vi.advanceTimersToNextFrame();
expect(frameRendered).toBe(true);
vi.getTimerCount
- Tipo:
() => number
Obtiene el número de temporizadores en espera.
vi.clearAllTimers
Elimina todos los temporizadores que están programados para ejecutarse. Estos temporizadores nunca se ejecutarán en el futuro.
vi.getMockedSystemTime
- Tipo:
() => Date | null
Devuelve la fecha actual simulada que se estableció usando setSystemTime
. Si la fecha no está simulada, el método devolverá null
.
vi.getRealSystemTime
- Tipo:
() => number
Cuando se usa vi.useFakeTimers
, las llamadas a Date.now
se simulan. Si necesitas obtener la hora real en milisegundos, puedes llamar a esta función.
vi.runAllTicks
- Tipo:
() => Vitest
Llama a cada microtarea que fue encolada por process.nextTick
. Esto también ejecutará todas las microtareas programadas por sí mismas.
vi.runAllTimers
- Tipo:
() => Vitest
Este método invocará cada temporizador iniciado hasta que la cola de temporizadores esté vacía. Esto significa que cada temporizador llamado durante runAllTimers
se disparará. Si tienes un intervalo infinito, lanzará una excepción después de 10.000 intentos (se puede configurar con fakeTimers.loopLimit
).
let i = 0;
setTimeout(() => console.log(++i));
const interval = setInterval(() => {
console.log(++i);
if (i === 3) {
clearInterval(interval);
}
}, 50);
vi.runAllTimers();
// log: 1
// log: 2
// log: 3
vi.runAllTimersAsync
- Tipo:
() => Promise<Vitest>
Este método invocará asíncronamente cada temporizador iniciado hasta que la cola de temporizadores esté vacía. Esto significa que cada temporizador llamado durante runAllTimersAsync
se disparará, incluso los temporizadores asíncronos. Si tienes un intervalo infinito, lanzará una excepción después de 10.000 intentos (se puede configurar con fakeTimers.loopLimit
).
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- Tipo:
() => Vitest
Este método llamará a cada temporizador que se inició después de la llamada a vi.useFakeTimers
. No disparará ningún temporizador que se haya iniciado durante su llamada.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- Tipo:
() => Promise<Vitest>
Este método llamará asíncronamente a cada temporizador que se inició después de la llamada a vi.useFakeTimers
, incluso los asíncronos. No disparará ningún temporizador que se haya iniciado durante su llamada.
setTimeout(() => {
console.log(1);
}, 100);
setTimeout(() => {
Promise.resolve().then(() => {
console.log(2);
setInterval(() => {
console.log(3);
}, 40);
});
}, 10);
await vi.runOnlyPendingTimersAsync();
// log: 2
// log: 3
// log: 3
// log: 1
vi.setSystemTime
- Tipo:
(date: string | number | Date) => void
Si los temporizadores falsos están habilitados, este método simula que un usuario cambia el reloj del sistema (afectará a la API relacionada con la fecha como hrtime
, performance.now
o new Date()
) - sin embargo, no disparará ningún temporizador. Si los temporizadores falsos no están habilitados, este método solo simulará las llamadas a Date.*
.
Útil si necesitas probar algo que depende de la fecha actual, por ejemplo, llamadas a Luxon dentro de tu código.
Acepta los mismos argumentos de cadena y número que Date
.
const date = new Date(1998, 11, 19);
vi.useFakeTimers();
vi.setSystemTime(date);
expect(Date.now()).toBe(date.valueOf());
vi.useRealTimers();
vi.useFakeTimers
- Tipo:
(config?: FakeTimerInstallOpts) => Vitest
Para habilitar la simulación de temporizadores, debes llamar a este método. Envolverá todas las llamadas posteriores a temporizadores (como setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
, y Date
) hasta que se llame a vi.useRealTimers()
.
La simulación de nextTick
no es compatible cuando se ejecuta Vitest dentro de node:child_process
usando --pool=forks
. NodeJS usa process.nextTick
internamente en node:child_process
y se cuelga cuando se simula. La simulación de nextTick
es compatible cuando se ejecuta Vitest con --pool=threads
.
La implementación se basa internamente en @sinonjs/fake-timers
.
TIP
vi.useFakeTimers()
no simula automáticamente process.nextTick
. Pero puedes habilitarlo especificando la opción en el argumento toFake
: vi.useFakeTimers({ toFake: ['nextTick'] })
.
vi.isFakeTimers
- Tipo:
() => boolean
Devuelve true
si los temporizadores falsos están habilitados.
vi.useRealTimers
- Tipo:
() => Vitest
Cuando los temporizadores se agotan, puedes llamar a este método para devolver los temporizadores simulados a sus implementaciones originales. Todos los temporizadores que se programaron antes se descartarán.
Miscelánea
Un conjunto de funciones de ayuda útiles que Vitest proporciona.
vi.waitFor
- Tipo:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
Espera a que la devolución de llamada se ejecute correctamente. Si la devolución de llamada lanza un error o devuelve una promesa rechazada, seguirá esperando hasta que tenga éxito o se agote el tiempo de espera.
Esto es muy útil cuando necesitas esperar a que se complete alguna acción asíncrona, por ejemplo, cuando inicias un servidor y necesitas esperar a que se inicie.
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Servidor iniciado correctamente', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) {
throw new Error('Servidor no iniciado');
}
console.log('Servidor iniciado');
},
{
timeout: 500, // por defecto es 1000
interval: 20, // por defecto es 50
}
);
expect(server.isReady).toBe(true);
});
También funciona para devoluciones de llamada asíncronas
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('El elemento existe en el DOM', async () => {
// empezar a poblar el DOM
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// intentar obtener el elemento hasta que exista
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // por defecto es 1000
interval: 20, // por defecto es 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Si se usa vi.useFakeTimers
, vi.waitFor
llama automáticamente a vi.advanceTimersByTime(interval)
en cada devolución de llamada de verificación.
vi.waitUntil
- Tipo:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Esto es similar a vi.waitFor
, pero si la devolución de llamada lanza algún error, la ejecución se interrumpe inmediatamente y se recibe un mensaje de error. Si la devolución de llamada devuelve un valor falsy, la siguiente verificación continuará hasta que se devuelva un valor truthy. Esto es útil cuando necesitas esperar a que algo exista antes de dar el siguiente paso.
Mira el ejemplo a continuación. Podemos usar vi.waitUntil
para esperar a que el elemento aparezca en la página, y luego podemos hacer algo con el elemento.
import { expect, test, vi } from 'vitest';
test('El elemento se renderiza correctamente', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // por defecto es 1000
interval: 20, // por defecto es 50
});
// hacer algo con el elemento
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted
- Tipo:
<T>(factory: () => T) => T
Todas las declaraciones import
estáticas en los módulos ES se elevan a la parte superior del archivo, por lo que cualquier código que se defina antes de las importaciones en realidad se ejecutará después de que se evalúen las importaciones.
Sin embargo, puede ser útil invocar algunos efectos secundarios como simular fechas antes de importar un módulo.
Para evitar esta limitación, puedes reescribir las importaciones estáticas en dinámicas de esta manera:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
Al ejecutar vitest
, puedes hacer esto automáticamente usando el método vi.hoisted
.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
Este método devuelve el valor que fue devuelto por la factory. Puedes usar ese valor en tus factories vi.mock
si necesitas un acceso fácil a variables definidas localmente:
import { expect, vi } from 'vitest';
import { originalMethod } from './path/to/module.js';
const { mockedMethod } = vi.hoisted(() => {
return { mockedMethod: vi.fn() };
});
vi.mock('./path/to/module.js', () => {
return { originalMethod: mockedMethod };
});
mockedMethod.mockReturnValue(100);
expect(originalMethod()).toBe(100);
Ten en cuenta que este método también se puede llamar de forma asíncrona, incluso si tu entorno no admite top-level await
:
const promised = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- Tipo:
RuntimeConfig
Actualiza la configuración para el archivo de prueba actual. Este método solo admite opciones de configuración que afectarán al archivo de prueba actual:
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// admite todo el objeto
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// solo admite "sequence.hooks"
},
});
vi.resetConfig
- Tipo:
RuntimeConfig
Si se llamó a vi.setConfig
antes, esto restablecerá la configuración al estado original.