Vi
O Vitest oferece funções utilitárias para auxiliar no uso do vi
. Você pode acessá-lo globalmente (quando a configuração de globais está ativada) ou importá-lo diretamente do vitest
:
import { vi } from 'vitest';
Módulos Mock
Esta seção descreve a API que você pode usar ao simular um módulo. Atenção: o Vitest não suporta a simulação de módulos importados usando 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 caminho. A chamada para vi.mock
é elevada (hoisted), portanto, não importa onde você a chama, ela sempre será executada antes de todas as importações. Se precisar referenciar variáveis fora do escopo, defina-as dentro de vi.hoisted
e as referencie 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 que vi.mock
seja elevado, o Vitest analisa estaticamente seus arquivos. Isso significa que vi
que 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 do vitest
, ou ative a opção de configuração globals
.
O Vitest não simulará módulos importados em 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 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.
Diferente 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.
Você também pode fornecer um objeto com uma propriedade spy
em vez de uma função factory
. Se spy
for true
, o Vitest fará a simulação automática do módulo normalmente, 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 uma promessa de módulo em vez de uma string nos métodos vi.mock
e vi.doMock
para melhor suporte de IDE. Quando o arquivo é movido, o caminho 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(); // o tipo é inferido
// ^?
return {
...mod,
// substitui algumas exportações
total: vi.fn(),
};
});
Internamente, o Vitest ainda opera em uma string e não em um objeto de módulo.
No entanto, se você estiver usando TypeScript com aliases paths
configurados em tsconfig.json
, o compilador não conseguirá resolver corretamente os tipos de importação. Para funcionar, substitua todas as importações com alias por seus caminhos relativos correspondentes. Ex: use import('./path/to/module.js')
em vez de import('@/module')
.
WARNING
vi.mock
é elevado (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á realmente chamado antes disso.
Isso significa que você não pode usar variáveis definidas fora da factory
dentro dela.
Se você precisar usar variáveis dentro da factory
, tente vi.doMock
. Ele funciona da mesma forma, mas não é elevado. Cuidado que ele apenas simula 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 simulando um módulo com exportação padrão, precisará fornecer uma chave default
dentro do objeto da função factory
retornada. 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á simulando, 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 simulando 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 arquivo:
- __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:
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
Atenção: se você não chamar vi.mock
, os módulos não são simulados automaticamente. Para replicar o comportamento de simulação automática 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á a simulação automática de todas as suas exportações. Para as regras aplicadas, consulte simulação automática.
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 é elevado 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á simulada.
WARNING
Isso não simulará módulos que foram importados antes desta chamada. Não se esqueça que todas as importações estáticas em ESM são sempre elevadas, então colocar isso antes da importação estática não forçará que seja chamado antes da importação:
vi.doMock('./increment.js'); // isso será chamado _depois_ da declaração de importação
import { increment } from './increment.js';
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// o módulo não é simulado, 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('importar o próximo módulo importa o simulado', async () => {
// a importação original NÃO FOI SIMULADA, 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 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>
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 simulados. Você pode passar { deep: true }
como segundo argumento para dizer ao TypeScript que o objeto inteiro é simulado, se realmente for.
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 é igual a 10', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('valor de retorno simulado com tipagem apenas parcialmente correta', 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 }) // 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 simulado. Pode ser útil se você quiser simular o 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 um módulo com todas as suas propriedades (incluindo propriedades aninhadas) simuladas. Segue as mesmas regras que vi.mock
. Para as regras aplicadas, consulte simulação automática.
vi.unmock
- Tipo:
(path: string | Promise<Module>) => void
Remove o módulo do registro de simulações. Todas as chamadas para importação retornarão o módulo original, mesmo que ele tenha sido simulado antes. Esta chamada é elevada para o topo do arquivo, então ela só irá remover a simulação de módulos que foram definidos em setupFiles
, por exemplo.
vi.doUnmock
- Tipo:
(path: string | Promise<Module>) => void
O mesmo que vi.unmock
, mas não é elevado para o topo do arquivo. A próxima importação do módulo importará o módulo original em vez da simulação. Isso não irá remover a simulação de módulos importados anteriormente.
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment já está simulado, porque vi.mock é elevado
increment(1) === 100;
// isso é elevado, e a factory é chamada antes da importação na linha 1
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// todas as chamadas são simuladas, e `increment` sempre retorna 100
increment(1) === 100;
increment(30) === 100;
// isso não é elevado, então outra importação retornará o módulo não simulado
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 é simulada, agora `increment` é a função original que retorna count + 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('alterar estado', async () => {
const mod = await import('./some/path.js'); // Será reavaliado
mod.changeLocalState('new value');
expect(mod.getLocalState()).toBe('new value');
});
test('módulo tem estado antigo', async () => {
const mod = await import('./some/path.js'); // Será reavaliado
expect(mod.getLocalState()).toBe('old value');
});
WARNING
Não redefine o registro de simulações. Para limpar o registro de simulações, use vi.unmock
ou vi.doUnmock
.
vi.dynamicImportSettled
Aguarda o carregamento de todas as importações. Útil, se você tiver uma chamada síncrona que inicia a importação de um módulo que você não pode esperar 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('operações são resolvidas', 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 aguardará até que todas sejam resolvidas.
Este método também aguardará o 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.
Simulação de Funções e Objetos
Esta seção descreve como trabalhar com simulações de método e substituir variáveis de ambiente e globais.
vi.fn
- Tipo:
(fn?: Function) => Mock
Cria um spy
em uma função, embora possa ser iniciado sem uma. 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, a simulação retornará undefined
quando invocada.
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 as propriedades e métodos de um determinado objeto da mesma forma que vi.mock()
simula as exportações de módulos. Veja simulação automática para detalhes.
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
Verifica se um determinado parâmetro é uma função simulada. Se você estiver usando TypeScript, ele também restringirá seu tipo.
vi.clearAllMocks
Chama .mockClear()
em todos os spies
. Isso limpará o histórico da simulação sem afetar as implementações da simulação.
vi.resetAllMocks
Chama .mockReset()
em todos os spies
. Isso limpará o histórico da simulação e redefinirá a implementação de cada simulação para a original.
vi.restoreAllMocks
Chama .mockRestore()
em todos os spies
. Isso limpará o histórico da simulação, restaurará todas as implementações originais da simulação e restaurará os descritores originais dos objetos espiados.
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 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
Em ambientes que suportam Gerenciamento Explícito de Recursos, você pode usar using
em vez de const
para chamar automaticamente mockRestore
em qualquer função simulada quando o bloco contendo for encerrado. Isso é especialmente útil para métodos espiados:
it('chama console.log', () => {
using spy = vi.spyOn(console, 'log').mockImplementation(() => {})
debug('mensagem')
expect(spy).toHaveBeenCalled()
})
// console.log é restaurado aqui
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 simulará 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 outras variáveis de ambiente
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 armazenará, 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 armazenará, 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 seja 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 seja 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
Chamará o próximo timer disponível. Útil para fazer asserções entre cada chamada de timer. Você pode encadear chamadas 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>
Chamará o próximo timer disponível e aguardará até que seja resolvido, se tiver sido definido assincronamente. Útil para fazer asserçõ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 os 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 aguardando.
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 simulada. Se a data não for simulada, o método retornará null
.
vi.getRealSystemTime
- Tipo:
() => number
Ao usar vi.useFakeTimers
, as chamadas Date.now
são simuladas. Se você precisar obter o tempo real em milissegundos, pode chamar esta função.
vi.runAllTicks
- Tipo:
() => Vitest
Chama todos os ticks
que foram enfileirados por process.nextTick
. Isso também executará todas as microtarefas agendadas por elas mesmas.
vi.runAllTimers
- Tipo:
() => Vitest
Este método invocará cada timer iniciado até que a fila de timers esteja vazia. Isso significa que cada timer chamado durante runAllTimers
será disparado. 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, mesmo os 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 que foi iniciado após a chamada vi.useFakeTimers
, mesmo 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 os timers falsos estiverem ativados, este método simula um usuário alterando o relógio do sistema (afetará APIs relacionadas a datas como hrtime
, performance.now
ou new Date()
) - no entanto, ele não disparará nenhum timer. Se os timers falsos não estiverem ativados, este método apenas simulará chamadas Date.*
.
Útil se você precisar testar algo que depende da data atual - por exemplo, chamadas Luxon no 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 a simulação 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.
A simulação de nextTick
não é suportada 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 é simulado. A simulação de nextTick
é suportada ao executar o Vitest com --pool=threads
.
A implementação é baseada internamente em @sinonjs/fake-timers
.
TIP
vi.useFakeTimers()
não simula automaticamente process.nextTick
e queueMicrotask
. Mas você pode habilitá-lo especificando a opção no argumento toFake
: vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] })
.
vi.isFakeTimers
- Tipo:
() => boolean
Retorna true
se os timers falsos estiverem ativados.
vi.useRealTimers
- Tipo:
() => Vitest
Quando os timers se esgotarem, você pode chamar este método para retornar os timers simulados à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>
Aguarda o callback
ser executado com sucesso. Se o callback
lançar um erro ou retornar uma promessa rejeitada, ele continuará aguardando até que seja bem-sucedido ou atinja o tempo limite.
Se as opções forem definidas como um número, o efeito é equivalente a definir { timeout: options }
.
Isso é muito útil quando você precisa esperar que alguma ação assíncrona seja concluída, 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('Servidor iniciado com sucesso', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) {
throw new Error('Servidor não iniciado');
}
console.log('Servidor iniciado');
},
{
timeout: 500, // padrão é 1000
interval: 20, // padrão é 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('Elemento existe no DOM', async () => {
// começa a popular o 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, // padrão é 1000
interval: 20, // padrão é 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 falso, a próxima verificação continuará até que um valor verdadeiro 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, em seguida, podemos fazer algo com o elemento.
import { expect, test, vi } from 'vitest';
test('Elemento renderiza corretamente', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // padrão é 1000
interval: 20, // padrão é 50
});
// faz algo com o elemento
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted
- Tipo:
<T>(factory: () => T) => T
Todas as declarações import
estáticas em módulos ES são elevadas para o topo do arquivo, então qualquer código que seja definido antes das importações será realmente executado após as importações serem avaliadas.
No entanto, pode ser útil invocar alguns efeitos colaterais, como simular 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
. Internamente, o Vitest converterá importações estáticas em dinâmicas com live-bindings preservados.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
IMPORTAÇÕES NÃO ESTÃO DISPONÍVEIS
Executar código antes das importações significa que você não pode acessar variáveis importadas porque elas ainda não foram definidas:
import { value } from './some/module.js';
vi.hoisted(() => { value }); // lança um erro
Este código produzirá um erro:
Não é possível acessar '__vi_import_0__' antes da inicialização
Se você precisar acessar uma variável de outro módulo dentro de vi.hoisted
, use a importação dinâmica:
await vi.hoisted(async () => {
const { value } = await import('./some/module.js');
});
No entanto, é desencorajado importar qualquer coisa dentro de vi.hoisted
porque as importações já são elevadas - se você precisar executar algo antes que os testes estejam em execução, apenas execute-o no próprio módulo importado.
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 json = 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.