Vi
Vitest proporciona funciones de utilidad a través de su asistente vi
. Puedes acceder a él globalmente (si la configuración global está habilitada) o importarlo directamente desde vitest
:
import { vi } from 'vitest';
Módulos Mock
Esta sección describe la API que puedes utilizar al simular un módulo. Ten en cuenta que Vitest no soporta la simulación de módulos importados mediante 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 utilizar los alias de Vite configurados dentro de una ruta. La llamada a vi.mock
se eleva, lo que significa que se ejecutará siempre antes de todas las importaciones, independientemente de dónde la coloques. Si necesitas referenciar variables externas a su ámbito, puedes definirlas dentro de vi.hoisted
y luego referenciarlas en vi.mock
.
WARNING
vi.mock
solo funciona para módulos importados con la palabra clave import
. No es compatible con require
.
Para elevar vi.mock
, Vitest analiza estáticamente tus archivos. Esto implica que vi
no puede ser utilizado si no se importa directamente del paquete vitest
(por ejemplo, desde un archivo de utilidad). Utiliza vi.mock
con vi
importado de vitest
, o habilita la opción de configuración globals
.
Vitest no simulará módulos importados dentro de un archivo de configuración 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 invoca la fábrica una única vez y almacena en caché los resultados para todas las importaciones subsiguientes hasta que se llama a vi.unmock
o vi.doUnmock
.
A diferencia de Jest, la fábrica puede ser asíncrona. Puedes usar vi.importActual
o una función auxiliar que reciba la fábrica como primer argumento para obtener el módulo original.
También puedes proporcionar un objeto con una propiedad spy
en lugar de una función de fábrica. Si spy
es true
, Vitest simulará automáticamente el módulo de forma habitual, pero no anulará la implementación de las exportaciones. Esto es útil si solo deseas verificar que el método exportado fue invocado correctamente por otro método.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
// Invoca la implementación original,
// pero permite verificar el comportamiento posteriormente
const result = calculator(1, 2);
expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
Vitest también soporta una promesa de módulo en lugar de una cadena en los métodos vi.mock
y vi.doMock
para una mejor integración con IDEs. Cuando el archivo se mueve, la ruta se actualizará, y importOriginal
inferirá el tipo automáticamente. El uso de esta firma también forzará que el tipo de retorno de la fábrica 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 utilizando 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 (es decir, se mueve) a la parte superior del archivo. Esto significa que, independientemente de dónde lo escribas (ya sea dentro de beforeEach
o test
), se invocará antes.
Esto también implica que no puedes usar ninguna variable definida fuera de la fábrica dentro de la misma.
Si necesitas usar variables dentro de la fábrica, considera vi.doMock
. Funciona de la misma manera pero no se eleva. Ten en cuenta que solo simula importaciones posteriores.
También puedes referenciar 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 predeterminada, deberás proporcionar una clave default
dentro del objeto devuelto por la función de fábrica. Esta es una particularidad específica del módulo 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 existe una carpeta __mocks__
junto a un archivo que estás simulando, y la fábrica no se proporciona, Vitest intentará encontrar un archivo con el mismo nombre en la subcarpeta __mocks__
y lo utilizará como un módulo real. Si estás simulando una dependencia, Vitest intentará encontrar una carpeta __mocks__
en la raíz del proyecto (el valor predeterminado es process.cwd()
). Puedes indicar a Vitest dónde se encuentran las dependencias a través de la opción de configuración deps.moduleDirectories
.
Por ejemplo, si 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 una fábrica u opciones proporcionadas, buscará un archivo en la carpeta __mocks__
para usarlo como módulo:
import { vi } from 'vitest';
// axios es una exportación predeterminada de `__mocks__/axios.js`
import axios from 'axios';
// increment es una exportación con nombre 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 fábrica proporcionada, Vitest importará el módulo original y simulará automáticamente todas sus exportaciones. Para las reglas aplicadas, consulta el 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
Similar a vi.mock
, pero no se eleva a la parte superior del archivo, lo que te permite referenciar variables en el ámbito global del archivo. La siguiente importación dinámica del módulo será simulada.
WARNING
Esto no simulará módulos que fueron importados antes de que se ejecutara esta llamada. No olvides que todas las importaciones estáticas en ESM siempre se elevan, por lo que colocar 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';
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 fábrica
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>
Utilidad de tipo para TypeScript. Simplemente devuelve el objeto que se le 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 indicar a TypeScript que todo el objeto está simulado, si realmente lo está.
export function add(x: number, y: number): number {
return x + y;
}
export function fetchSomething(): Promise<Response> {
return fetch('https://vitest.dev/');
}
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('valor de retorno simulado con tipado 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 de si debe ser simulado. Puede ser útil si quieres simular un módulo parcialmente.
vi.mock('./example.js', async () => {
const originalModule = await vi.importActual('./example.js');
return { ...originalModule, 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 las reglas aplicadas, consulta el algoritmo.
vi.unmock
- Tipo:
(path: string | Promise<Module>) => void
Elimina el módulo del registro de simulaciones. Todas las llamadas para importar devolverán el módulo original, incluso si se simuló previamente. 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
Similar a 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.
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 fábrica 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á un módulo no simulado
vi.doUnmock('./increment.js');
// esto SIGUE devolviendo 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 cuando se reimportan. Las importaciones de nivel superior no se pueden reevaluar. Puede ser útil para aislar módulos cuyo 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 borrar el registro de simulaciones, usa vi.unmock
o vi.doUnmock
.
vi.dynamicImportSettled
Espera a que todas las importaciones se carguen. Útil si tienes una llamada síncrona que inicia la importación de un módulo y no hay otra forma de esperar a que se complete.
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, este método esperará hasta que todas se resuelvan.
Este método también esperará al siguiente ciclo de setTimeout
después de que la importación se resuelva para que todas las operaciones síncronas se completen.
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 puede ser instanciada sin una. Cada vez que se invoca una función, almacena sus argumentos de llamada, valores de retorno 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.mockObject 3.2.0+
- Tipo:
<T>(value: T) => MaybeMockedDeep<T>
Simula profundamente las propiedades y métodos de un objeto dado de la misma manera que vi.mock()
simula las exportaciones de módulos. Consulta automocking para más detalles.
const original = {
simple: () => 'value',
nested: {
method: () => 'real',
},
prop: 'foo',
};
const mocked = vi.mockObject(original);
expect(mocked.simple()).toBe(undefined);
expect(mocked.nested.method()).toBe(undefined);
expect(mocked.prop).toBe('foo');
mocked.simple.mockReturnValue('mocked');
mocked.nested.method.mockReturnValue('mocked nested');
expect(mocked.simple()).toBe('mocked');
expect(mocked.nested.method()).toBe('mocked nested');
vi.isMockFunction
- Tipo:
(fn: Function) => boolean
Comprueba si un parámetro dado es una función simulada. Si estás usando TypeScript, también restringirá su tipo.
vi.clearAllMocks
Llama a .mockClear()
en todos los espías. Esto borrará el historial de simulaciones sin afectar las implementaciones de las simulaciones.
vi.resetAllMocks
Llama a .mockReset()
en todos los espías. Esto borrará el historial de simulaciones y restablecerá la implementación de cada simulación a su original.
vi.restoreAllMocks
Llama a .mockRestore()
en todos los espías. Esto borrará el historial de simulaciones, restaurará todas las implementaciones originales de las simulaciones y restaurará los descriptores originales de los objetos espiados.
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
En entornos que admiten Gestión explícita de recursos, puedes usar using
en lugar de const
para llamar automáticamente a mockRestore
en cualquier función simulada cuando se sale del bloque que la contiene. Esto es especialmente útil para métodos espiados:
it('llama a console.log', () => {
using spy = vi.spyOn(console, 'log').mockImplementation(() => {})
debug('mensaje')
expect(spy).toHaveBeenCalled()
})
// console.log se restaura aquí
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()); // ¡sigue siendo 42!
TIP
No es posible espiar métodos exportados en el 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 verificar 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 la 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 otros entornos
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 conservará 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 conservará 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 simulados.
vi.advanceTimersByTime
- Tipo:
(ms: number) => Vitest
Este método invocará cada temporizador iniciado hasta que el número de milisegundos especificado haya pasado 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 el número de milisegundos especificado haya pasado o la cola esté vacía, lo que ocurra primero. Esto incluirá temporizadores configurados asincrónicamente.
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
Ejecutará el siguiente temporizador disponible. Útil para realizar aserciones entre cada llamada al temporizador. Puedes encadenar sus llamadas para gestionar los temporizadores por ti mismo.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Tipo:
() => Promise<Vitest>
Ejecutará el siguiente temporizador disponible y esperará hasta que se resuelva si se configuró de forma asíncrona. Útil para realizar aserciones 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 los callbacks actualmente programados 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 programados para ejecutarse. Estos temporizadores nunca se ejecutarán en el futuro.
vi.getMockedSystemTime
- Tipo:
() => Date | null
Devuelve la fecha actual simulada. 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 puesta en cola por process.nextTick
. Esto también ejecutará todas las microtareas que se programaron a sí mismas.
vi.runAllTimers
- Tipo:
() => Vitest
Este método ejecutará cada temporizador iniciado hasta que la cola de temporizadores esté vacía. Esto significa que todos los temporizadores llamados durante runAllTimers
se ejecutarán. Si tienes un intervalo infinito, lanzará un error 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á de forma asíncrona cada temporizador iniciado hasta que la cola de temporizadores esté vacía. Esto significa que cada temporizador llamado durante runAllTimersAsync
se ejecutará, incluso los temporizadores asíncronos. Si tienes un intervalo infinito, lanzará un error 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 ejecutará cada temporizador que se inició después de la llamada a vi.useFakeTimers
. No ejecutará 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 ejecutará de forma asíncrona cada temporizador que se inició después de la llamada a vi.useFakeTimers
, incluidos los asíncronos. No ejecutará 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 simulados están habilitados, este método simula que un usuario cambia el reloj del sistema (afectará la API relacionada con la fecha como hrtime
, performance.now
o new Date()
); sin embargo, no ejecutará ningún temporizador. Si los temporizadores simulados no están habilitados, este método solo simulará las llamadas a Date.*
.
Útil si necesitas probar algo que dependa 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. Interceptará 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 bloquea 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
y queueMicrotask
. Pero puedes habilitarlo especificando la opción en el argumento toFake
: vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] })
.
vi.isFakeTimers
- Tipo:
() => boolean
Devuelve true
si los temporizadores simulados están habilitados.
vi.useRealTimers
- Tipo:
() => Vitest
Cuando los temporizadores se han agotado, puedes llamar a este método para restaurar 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 el callback se ejecute correctamente. Si el callback lanza un error o devuelve una promesa rechazada, seguirá esperando hasta que tenga éxito o se agote el tiempo de espera.
Si las opciones se establecen en un número, el efecto es equivalente a establecer { timeout: options }
.
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, // el valor predeterminado es 1000
interval: 20, // el valor predeterminado es 50
}
);
expect(server.isReady).toBe(true);
});
También funciona para callbacks asíncronos
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('El elemento existe en un 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, // el valor predeterminado es 1000
interval: 20, // el valor predeterminado es 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Si se usa vi.useFakeTimers
, vi.waitFor
llama automáticamente a vi.advanceTimersByTime(interval)
en cada callback de verificación.
vi.waitUntil
- Tipo:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Esto es similar a vi.waitFor
, pero si el callback lanza algún error, la ejecución se interrumpe inmediatamente y se recibe un mensaje de error. Si el callback 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, // el valor predeterminado es 1000
interval: 20, // el valor predeterminado 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 se ejecutará realmente después de que se evalúen las importaciones.
Sin embargo, puede ser útil invocar algunos efectos secundarios como la simulación de 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
. Internamente, Vitest convertirá las importaciones estáticas en dinámicas, preservando los live-bindings.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
LAS IMPORTACIONES NO ESTÁN DISPONIBLES
Ejecutar código antes de las importaciones significa que no puedes acceder a las variables importadas porque aún no están definidas:
import { value } from './some/module.js';
vi.hoisted(() => { value }); // lanza un error
Este código producirá un error:
No se puede acceder a '__vi_import_0__' antes de la inicialización
Si necesitas acceder a una variable de otro módulo dentro de vi.hoisted
, usa la importación dinámica:
await vi.hoisted(async () => {
const { value } = await import('./some/module.js');
});
Sin embargo, no se recomienda importar nada dentro de vi.hoisted
porque las importaciones ya están elevadas; si necesitas ejecutar algo antes de que se ejecuten las pruebas, simplemente ejecútalo en el propio módulo importado.
Este método devuelve el valor que fue devuelto por la fábrica. Puedes usar ese valor en tus fábricas 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 puede llamarse de forma asíncrona incluso si tu entorno no admite top-level await
:
const json = 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),
// soporta todo el objeto
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// soporta solo "sequence.hooks"
},
});
vi.resetConfig
- Tipo:
RuntimeConfig
Si vi.setConfig
fue llamado previamente, esto restablecerá la configuración a su estado original.