vi
O Vitest oferece funções utilitárias para auxiliar no uso do vi
. Você pode acessá-lo globalmente (quando a configuração globals está habilitada) ou importá-lo diretamente de vitest
:
import { vi } from 'vitest';
Mock de Módulos
Esta seção descreve a API que você pode usar ao mockar um módulo. Atenção: o Vitest não suporta o mock de módulos importados com 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
Substitui todos os módulos importados do path
fornecido por outro módulo. Você pode usar aliases do Vite configurados dentro de um path. A chamada para vi.mock
é içada (hoisted), portanto, não importa onde você a chama. Ela sempre será executada antes de todas as importações. Se você precisar referenciar algumas variáveis fora do seu escopo, você pode defini-las dentro de vi.hoisted
e referenciá-las dentro de vi.mock
.
WARNING
vi.mock
funciona apenas para módulos que foram importados com a palavra-chave import
. Não funciona com require
.
Para içar vi.mock
, o Vitest analisa estaticamente seus arquivos. Isso significa que vi
, se não foi importado diretamente do pacote vitest
(por exemplo, de algum arquivo utilitário), não pode ser usado. Use vi.mock
com vi
importado de vitest
, ou habilite a opção de configuração globals
.
O Vitest não mockará módulos importados dentro de um arquivo de configuração (setup file), pois eles são armazenados em cache quando um arquivo de teste é executado. Você pode chamar vi.resetModules()
dentro de vi.hoisted
para limpar todos os caches de módulo antes de executar um arquivo de teste.
Se a função factory
estiver definida, todas as importações retornarão o seu resultado. O Vitest chama a factory apenas uma vez e armazena os resultados em cache para todas as importações subsequentes até que vi.unmock
ou vi.doUnmock
seja chamado.
Ao contrário do jest
, a factory pode ser assíncrona. Você pode usar vi.importActual
ou um auxiliar com a factory passada como primeiro argumento, e obter o módulo original dentro.
Desde o Vitest 2.1, você também pode fornecer um objeto com uma propriedade spy
em vez de uma função factory. Se spy
for true
, então o Vitest automockará o módulo como de costume, mas não substituirá a implementação das exportações. Isso é útil se você quiser apenas verificar se o método exportado foi chamado corretamente por outro método.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
// chama a implementação original,
// mas permite afirmar o comportamento mais tarde
const result = calculator(1, 2);
expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
O Vitest também suporta o uso de uma promessa de módulo em vez de uma string nos métodos vi.mock
e vi.doMock
, proporcionando melhor suporte para IDE. Quando o arquivo é movido, o path será atualizado, e importOriginal
herda o tipo automaticamente. Usar esta assinatura também forçará o tipo de retorno da factory a ser compatível com o módulo original (mantendo as exportações opcionais).
// @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(); // type is inferred
// ^?
return {
...mod,
// replace some exports
total: vi.fn(),
};
});
Internamente, o Vitest ainda opera em uma string e não em um objeto de módulo.
Se você estiver usando TypeScript com aliases paths
configurados em tsconfig.json
, no entanto, o compilador pode não conseguir resolver corretamente os tipos de importação. Para fazer isso funcionar, certifique-se de substituir todas as importações com alias por seus paths relativos correspondentes. Ex: use import('./path/to/module.js')
em vez de import('@/module')
.
WARNING
vi.mock
é içado (em outras palavras, movido) para o topo do arquivo. Isso significa que, sempre que você o escrever, seja dentro de beforeEach
ou test
, ele será chamado antes disso.
Isso também significa que você não pode usar nenhuma variável dentro da factory que esteja definida fora da factory.
Se você precisar usar variáveis dentro da factory, tente vi.doMock
. Ele funciona da mesma forma, mas não é içado. Observe que ele apenas mocka importações subsequentes.
Você também pode referenciar variáveis definidas pelo método vi.hoisted
se ele foi declarado 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
Se você estiver mockando um módulo com exportação padrão, precisará fornecer uma chave default
no objeto retornado pela função factory. Esta é uma ressalva específica do módulo ES; portanto, a documentação do jest
pode diferir, pois o jest
usa módulos CommonJS. Por exemplo,
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// etc...
};
});
Se houver uma pasta __mocks__
ao lado de um arquivo que você está mockando, e a factory não for fornecida, o Vitest tentará encontrar um arquivo com o mesmo nome na subpasta __mocks__
e usá-lo como um módulo real. Se você estiver mockando uma dependência, o Vitest tentará encontrar uma pasta __mocks__
na raiz do projeto (o padrão é process.cwd()
). Você pode dizer ao Vitest onde as dependências estão localizadas através da opção de configuração deps.moduleDirectories
.
Por exemplo, você tem esta estrutura de arquivos:
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
Se você chamar vi.mock
em um arquivo de teste sem uma factory ou opções fornecidas, ele encontrará um arquivo na pasta __mocks__
para usar como módulo:
// increment.test.js
import { vi } from 'vitest';
// axios é uma exportação padrão de `__mocks__/axios.js`
import axios from 'axios';
// increment é uma exportação nomeada de `src/__mocks__/increment.js`
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
Observe que, se você não chamar vi.mock
, os módulos não são mockados automaticamente. Para replicar o comportamento de automocking do Jest, você pode chamar vi.mock
para cada módulo necessário dentro de setupFiles
.
Se não houver uma pasta __mocks__
ou uma factory fornecida, o Vitest importará o módulo original e fará o automock de todas as suas exportações. Para as regras aplicadas, veja 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
O mesmo que vi.mock
, mas não é içado para o topo do arquivo, então você pode referenciar variáveis no escopo global do arquivo. A próxima importação dinâmica do módulo será mockada.
WARNING
Isso não mockará módulos que foram importados antes de ser chamado. Não se esqueça que todas as importações estáticas em ESM são sempre içadas, então colocar isso antes de uma importação estática não fará com que seja chamado antes da importação:
vi.doMock('./increment.js'); // isso será chamado _depois_ da instrução import
import { increment } from './increment.js';
// ./increment.js
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// o módulo não é mockado, porque vi.doMock ainda não foi chamado
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// você pode acessar variáveis dentro de uma factory
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('importing the next module imports mocked one', async () => {
// a importação original NÃO FOI MOCKADA, porque vi.doMock é avaliado DEPOIS das importações
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// a nova importação dinâmica retorna o módulo mockado
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>
Auxiliar de tipo para TypeScript. Apenas retorna o objeto que foi passado.
Quando partial
é true
, ele esperará um Partial<T>
como valor de retorno. Por padrão, isso fará com que o TypeScript acredite que apenas os valores de primeiro nível são mockados. Você pode passar { deep: true }
como segundo argumento para dizer ao TypeScript que o objeto inteiro é mockado, se for o caso.
// 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 equals 10', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('mock return value with only partially correct typing', async () => {
vi.mocked(example.fetchSomething).mockResolvedValue(new Response('hello'));
vi.mocked(example.fetchSomething, { partial: true }).mockResolvedValue({
ok: false,
});
// isso é um erro de tipo
});
vi.importActual
- Tipo:
<T>(path: string) => Promise<T>
Importa o módulo, ignorando todas as verificações se ele deve ser mockado. Pode ser útil se você quiser mockar o 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 um módulo com todas as suas propriedades (incluindo propriedades aninhadas) mockadas. Segue as mesmas regras que vi.mock
. Para as regras aplicadas, veja algoritmo.
vi.unmock
- Tipo:
(path: string | Promise<Module>) => void
Remove o módulo do registro de mockados. Todas as chamadas para importação retornarão o módulo original, mesmo que tenha sido mockado antes. Esta chamada é içada para o topo do arquivo, então ela apenas desmockará módulos definidos em setupFiles
, por exemplo.
vi.doUnmock
- Tipo:
(path: string | Promise<Module>) => void
O mesmo que vi.unmock
, mas não é içado para o topo do arquivo. A próxima importação do módulo importará o módulo original em vez do mock. Isso não desmockará módulos importados anteriormente.
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment já está mockado, porque vi.mock é içado
increment(1) === 100;
// isso é içado, e a factory é chamada antes da importação na linha 1
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// todas as chamadas são mockadas, e `increment` sempre retorna 100
increment(1) === 100;
increment(30) === 100;
// isso não é içado, então uma importação subsequente retornará o módulo não mockado
vi.doUnmock('./increment.js');
// isso AINDA retorna 100, porque `vi.doUnmock` não reavalia um módulo
increment(1) === 100;
increment(30) === 100;
// a próxima importação não é mockada, agora `increment` é a função original que retorna o número + 1
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- Tipo:
() => Vitest
Reinicia o registro de módulos limpando o cache de todos os módulos. Isso permite que os módulos sejam reavaliados quando reimportados. Importações de nível superior não podem ser reavaliadas. Pode ser útil para isolar módulos onde o estado local entra em conflito entre os testes.
import { vi } from 'vitest';
import { data } from './data.js'; // Não será reavaliado antes de cada teste
beforeEach(() => {
vi.resetModules();
});
test('change state', async () => {
const mod = await import('./some/path.js'); // Será reavaliado
mod.changeLocalState('new value');
expect(mod.getLocalState()).toBe('new value');
});
test('module has old state', async () => {
const mod = await import('./some/path.js'); // Será reavaliado
expect(mod.getLocalState()).toBe('old value');
});
WARNING
Não reinicia o registro de mocks. Para limpar o registro de mocks, use vi.unmock
ou vi.doUnmock
.
vi.dynamicImportSettled
Espera que todas as importações dinâmicas sejam carregadas. Útil se você tiver uma chamada síncrona que inicia a importação de um módulo e não puder esperar por ela de outra forma.
import { expect, test } from 'vitest';
// não é possível rastrear a importação porque a Promise não é retornada
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('operations are resolved', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
Se durante uma importação dinâmica outra importação dinâmica for iniciada, este método esperará até que todas elas sejam resolvidas.
Este método também esperará pelo próximo tick de setTimeout
após a importação ser resolvida, para que todas as operações síncronas sejam concluídas no momento em que for resolvida.
Mock de Funções e Objetos
Esta seção descreve como trabalhar com mocks de métodos e substituir variáveis de ambiente e globais.
vi.fn
- Tipo:
(fn?: Function) => Mock
Cria um spy em uma função, embora possa ser criado sem uma função. Toda vez que uma função é invocada, ela armazena seus argumentos de chamada, retornos e instâncias. Além disso, você pode manipular seu comportamento com métodos. Se nenhuma função for fornecida, o mock retornará undefined
quando invocado.
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
Verifica se um determinado parâmetro é uma função mock. Se você estiver usando TypeScript, ele também restringirá seu tipo.
vi.clearAllMocks
Chama .mockClear()
em todos os spies. Isso limpará o histórico do mock, mas não redefinirá sua implementação para a padrão.
vi.resetAllMocks
Chama .mockReset()
em todos os spies. Isso limpará o histórico do mock e redefinirá sua implementação para uma função vazia (retornará undefined
).
vi.restoreAllMocks
Chama .mockRestore()
em todos os spies. Isso limpará o histórico do mock e redefinirá sua implementação para a original.
vi.spyOn
- Tipo:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Cria um spy em um método ou getter/setter de um objeto semelhante a vi.fn()
. Ele retorna uma função mock.
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
Você pode chamar vi.restoreAllMocks
dentro de afterEach
(ou habilitar test.restoreMocks
) para restaurar todos os métodos às suas implementações originais. Isso restaurará o descritor de objeto original, então você não poderá alterar a implementação do 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()); // ainda 42!
TIP
Não é possível espionar métodos exportados no modo navegador. Em vez disso, você pode espionar cada método exportado chamando vi.mock("./file-path.js", { spy: true })
. Isso mockará cada exportação, mas manterá sua implementação intacta, permitindo que você afirme se o método foi chamado corretamente.
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);
E embora seja possível espionar exportações em jsdom
ou outros ambientes Node.js, isso pode mudar no futuro.
vi.stubEnv
- Tipo:
<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
Altera o valor da variável de ambiente em process.env
e import.meta.env
. Você pode restaurar seu valor chamando vi.unstubAllEnvs
.
import { vi } from 'vitest';
// `process.env.NODE_ENV` e `import.meta.env.NODE_ENV`
// são "development" antes de chamar "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;
// não altera outros envs
import.meta.env.MODE === 'development';
TIP
Você também pode alterar o valor simplesmente atribuindo-o, mas não poderá usar vi.unstubAllEnvs
para restaurar o valor anterior:
import.meta.env.MODE = 'test';
vi.unstubAllEnvs
- Tipo:
() => Vitest
Restaura todos os valores de import.meta.env
e process.env
que foram alterados com vi.stubEnv
. Quando é chamado pela primeira vez, o Vitest lembra o valor original e o armazena, até que unstubAllEnvs
seja chamado novamente.
import { vi } from 'vitest';
// `process.env.NODE_ENV` e `import.meta.env.NODE_ENV`
// são "development" antes de chamar 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 para o valor que foi armazenado antes da primeira chamada "stubEnv"
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- Tipo:
(name: string | number | symbol, value: unknown) => Vitest
Altera o valor de uma variável global. Você pode restaurar seu valor original chamando vi.unstubAllGlobals
.
import { vi } from 'vitest';
// `innerWidth` é "0" antes de chamar stubGlobal
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// se você estiver usando jsdom ou happy-dom
window.innerWidth === 100;
TIP
Você também pode alterar o valor simplesmente atribuindo-o a globalThis
ou window
(se você estiver usando o ambiente jsdom
ou happy-dom
), mas não poderá usar vi.unstubAllGlobals
para restaurar o valor original:
globalThis.innerWidth = 100;
// se você estiver usando jsdom ou happy-dom
window.innerWidth = 100;
vi.unstubAllGlobals
- Tipo:
() => Vitest
Restaura todos os valores globais em globalThis
/global
(e window
/top
/self
/parent
, se você estiver usando o ambiente jsdom
ou happy-dom
) que foram alterados com vi.stubGlobal
. Quando é chamado pela primeira vez, o Vitest lembra o valor original e o armazena, até que unstubAllGlobals
seja chamado novamente.
import { vi } from 'vitest';
const Mock = vi.fn();
// IntersectionObserver é "undefined" antes de chamar "stubGlobal"
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// se você estiver usando jsdom ou happy-dom
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// lança ReferenceError, porque não está definido
IntersectionObserver === undefined;
Timers Falsos
Esta seção descreve como trabalhar com timers falsos.
vi.advanceTimersByTime
- Tipo:
(ms: number) => Vitest
Este método invocará cada timer iniciado até que o número especificado de milissegundos tenha passado ou a fila esteja vazia - o que ocorrer primeiro.
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 timer iniciado até que o número especificado de milissegundos tenha passado ou a fila esteja vazia - o que ocorrer primeiro. Isso incluirá timers definidos assincronamente.
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
Chama o próximo timer disponível. Útil para fazer afirmações entre cada chamada de timer. Você pode encadeá-lo para gerenciar os timers por conta própria.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Tipo:
() => Promise<Vitest>
Chama o próximo timer disponível e espera até que seja resolvido se foi definido assincronamente. Útil para fazer afirmações entre cada chamada de timer.
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
Semelhante a vi.advanceTimersByTime
, mas avançará os timers pelos milissegundos necessários para executar callbacks atualmente agendados com requestAnimationFrame
.
let frameRendered = false;
requestAnimationFrame(() => {
frameRendered = true;
});
vi.advanceTimersToNextFrame();
expect(frameRendered).toBe(true);
vi.getTimerCount
- Tipo:
() => number
Obtém o número de timers esperando.
vi.clearAllTimers
Remove todos os timers que estão agendados para serem executados. Esses timers nunca serão executados no futuro.
vi.getMockedSystemTime
- Tipo:
() => Date | null
Retorna a data atual mockada que foi definida usando setSystemTime
. Se a data não for mockada, o método retornará null
.
vi.getRealSystemTime
- Tipo:
() => number
Ao usar vi.useFakeTimers
, as chamadas Date.now
são mockadas. Se você precisar obter o tempo real em milissegundos, pode chamar esta função.
vi.runAllTicks
- Tipo:
() => Vitest
Chama todas as microtasks que foram enfileiradas por process.nextTick
. Isso também executará todas as microtasks agendadas por si mesmas.
vi.runAllTimers
- Tipo:
() => Vitest
Este método invocará cada timer iniciado até que a fila de timers esteja vazia. Isso significa que todos os timers chamados durante runAllTimers
serão executados. Se você tiver um intervalo infinito, ele lançará um erro após 10.000 tentativas (pode ser configurado com 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á assincronamente cada timer iniciado até que a fila de timers esteja vazia. Isso significa que cada timer chamado durante runAllTimersAsync
será disparado, incluindo timers assíncronos. Se você tiver um intervalo infinito, ele lançará um erro após 10.000 tentativas (pode ser configurado com fakeTimers.loopLimit
).
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- Tipo:
() => Vitest
Este método chamará cada timer que foi iniciado após a chamada vi.useFakeTimers
. Ele não disparará nenhum timer que foi iniciado durante sua chamada.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- Tipo:
() => Promise<Vitest>
Este método chamará assincronamente cada timer iniciado após a chamada vi.useFakeTimers
, incluindo os assíncronos. Ele não disparará nenhum timer que foi iniciado durante sua chamada.
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
Se timers falsos estiverem habilitados, este método simula um usuário alterando o relógio do sistema (afetará APIs relacionadas a data como hrtime
, performance.now
ou new Date()
) - no entanto, ele não disparará nenhum timer. Se timers falsos não estiverem habilitados, este método apenas mockará chamadas Date.*
.
Útil se você precisar testar algo que depende da data atual - por exemplo, chamadas Luxon dentro do seu código.
Aceita os mesmos argumentos de string e número que o 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 o mock de timers, você precisa chamar este método. Ele envolverá todas as chamadas futuras para timers (como setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
e Date
) até que vi.useRealTimers()
seja chamado.
O mock de nextTick
não é suportado ao executar o Vitest dentro de node:child_process
usando --pool=forks
. O NodeJS usa process.nextTick
internamente em node:child_process
e trava quando é mockado. O mock de nextTick
é suportado ao executar o Vitest com --pool=threads
.
A implementação é baseada internamente em @sinonjs/fake-timers
.
TIP
vi.useFakeTimers()
não mocka automaticamente process.nextTick
. Mas você pode habilitá-lo especificando a opção no argumento toFake
: vi.useFakeTimers({ toFake: ['nextTick'] })
.
vi.isFakeTimers
- Tipo:
() => boolean
Retorna true
se timers falsos estiverem habilitados.
vi.useRealTimers
- Tipo:
() => Vitest
Quando os timers terminarem de executar, você pode chamar este método para retornar os timers mockados às suas implementações originais. Todos os timers que foram agendados antes serão descartados.
Diversos
Um conjunto de funções auxiliares úteis que o Vitest fornece.
vi.waitFor
- Tipo:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
Espera que o callback seja executado com sucesso. Se o callback lançar um erro ou retornar uma promessa rejeitada, ele continuará esperando até que seja bem-sucedido ou atinja o tempo limite.
É muito útil quando você precisa aguardar a conclusão de uma ação assíncrona, por exemplo, quando você inicia um servidor e precisa esperar que ele inicie.
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Server started successfully', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) {
throw new Error('Server not started');
}
console.log('Server started');
},
{
timeout: 500, // default is 1000
interval: 20, // default is 50
}
);
expect(server.isReady).toBe(true);
});
Também funciona para callbacks assíncronos
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('Element exists in a DOM', async () => {
// inicia o preenchimento do DOM
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// tenta obter o elemento até que ele exista
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // default is 1000
interval: 20, // default is 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Se vi.useFakeTimers
for usado, vi.waitFor
chama automaticamente vi.advanceTimersByTime(interval)
em cada callback de verificação.
vi.waitUntil
- Tipo:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Isso é semelhante a vi.waitFor
, mas se o callback lançar algum erro, a execução é imediatamente interrompida e uma mensagem de erro é recebida. Se o callback retornar um valor falsy, a próxima verificação continuará até que um valor truthy seja retornado. Isso é útil quando você precisa esperar que algo exista antes de dar o próximo passo.
Veja o exemplo abaixo. Podemos usar vi.waitUntil
para esperar que o elemento apareça na página, e então podemos fazer algo com o elemento.
import { expect, test, vi } from 'vitest';
test('Element render correctly', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // default is 1000
interval: 20, // default is 50
});
// faz algo com o elemento
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted
- Tipo:
<T>(factory: () => T) => T
Todas as instruções import
estáticas em módulos ES são içadas para o topo do arquivo, então qualquer código definido antes das importações será realmente executado após a avaliação das importações.
No entanto, pode ser útil invocar alguns efeitos colaterais, como mockar datas antes de importar um módulo.
Para contornar essa limitação, você pode reescrever importações estáticas em dinâmicas assim:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
Ao executar vitest
, você pode fazer isso automaticamente usando o método vi.hoisted
.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
Este método retorna o valor que foi retornado da factory. Você pode usar esse valor em suas factories vi.mock
se precisar de acesso fácil a variáveis 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);
Observe que este método também pode ser chamado assincronamente, mesmo que seu ambiente não suporte 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
Atualiza a configuração para o arquivo de teste atual. Este método suporta apenas opções de configuração que afetarão o arquivo de teste atual:
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// suporta o objeto inteiro
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// suporta apenas "sequence.hooks"
},
});
vi.resetConfig
- Tipo:
RuntimeConfig
Se vi.setConfig
foi chamado antes, isso redefinirá a configuração para o estado original.