API do Runner
WARNING
Esta é uma API avançada. Se você deseja apenas executar testes, provavelmente não precisará dela. Ela é usada principalmente por autores de bibliotecas.
Você pode especificar o caminho para o seu executor de testes (runner) com a opção runner
no seu arquivo de configuração. Este arquivo deve ter uma exportação padrão que implemente um construtor de classe com os seguintes métodos:
export interface VitestRunner {
/**
* O primeiro método a ser chamado antes de realmente coletar e executar os testes.
*/
onBeforeCollect?: (paths: string[]) => unknown;
/**
* Chamado após a coleta dos testes e antes da execução de "onBeforeRun".
*/
onCollected?: (files: File[]) => unknown;
/**
* Chamado quando o executor de testes deve cancelar as próximas execuções de teste.
* O Runner deve monitorar este método e, quando ele for chamado, marcar os testes e suítes como ignorados em
* "onBeforeRunSuite" e "onBeforeRunTask".
*/
onCancel?: (reason: CancelReason) => unknown;
/**
* Chamado antes de executar um único teste. Ainda não possui "result".
*/
onBeforeRunTask?: (test: TaskPopulated) => unknown;
/**
* Chamado antes de realmente executar a função de teste. Já possui "result" com "state" e "startTime".
*/
onBeforeTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Chamado após o resultado e o estado serem definidos.
*/
onAfterRunTask?: (test: TaskPopulated) => unknown;
/**
* Chamado imediatamente após a execução da função de teste. Ainda não tem um novo estado. Não será chamado caso a função de teste gere uma exceção.
*/
onAfterTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Chamado antes de executar uma única suíte. Ainda não possui "result".
*/
onBeforeRunSuite?: (suite: Suite) => unknown;
/**
* Chamado após executar uma única suíte. Possui estado e resultado.
*/
onAfterRunSuite?: (suite: Suite) => unknown;
/**
* Se definido, será chamado em vez do particionamento e tratamento padrão da suíte Vitest.
* Os hooks "before" e "after" não serão ignorados.
*/
runSuite?: (suite: Suite) => Promise<void>;
/**
* Se definido, será chamado em vez do tratamento padrão do Vitest. Útil caso você tenha sua função de teste personalizada.
* Os hooks "before" e "after" não serão ignorados.
*/
runTask?: (test: TaskPopulated) => Promise<void>;
/**
* Chamado quando uma tarefa é atualizada. É o mesmo que "onTaskUpdate" em um reporter, mas este é executado no mesmo thread que os testes.
*/
onTaskUpdate?: (
task: [string, TaskResult | undefined, TaskMeta | undefined][]
) => Promise<void>;
/**
* Chamado antes de executar todos os testes nos caminhos coletados.
*/
onBeforeRunFiles?: (files: File[]) => unknown;
/**
* Chamado logo após executar todos os testes nos caminhos coletados.
*/
onAfterRunFiles?: (files: File[]) => unknown;
/**
* Chamado quando um novo contexto para um teste é definido. Útil para adicionar propriedades personalizadas ao contexto.
* Se você deseja apenas definir um contexto personalizado com um runner, considere usar "beforeAll" em "setupFiles".
*/
extendTaskContext?: (context: TestContext) => TestContext;
/**
* Chamado quando certos arquivos são importados. Pode ser chamado em duas situações: para coletar testes e para importar arquivos de configuração.
*/
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
/**
* Função chamada quando o runner tenta obter o valor ao usar `test.extend` com `{ injected: true }`
*/
injectValue?: (key: string) => unknown;
/**
* Configuração publicamente disponível.
*/
config: VitestRunnerConfig;
/**
* O nome do pool atual. Pode afetar como o stack trace é inferido no lado do servidor.
*/
pool?: string;
}
Ao instanciar esta classe, o Vitest passa sua configuração – você deve expô-la como uma propriedade config
:
import type { RunnerTestFile } from 'vitest';
import type { VitestRunner, VitestRunnerConfig } from 'vitest/suite';
import { VitestTestRunner } from 'vitest/runners';
class CustomRunner extends VitestTestRunner implements VitestRunner {
public config: VitestRunnerConfig;
constructor(config: VitestRunnerConfig) {
this.config = config;
}
onAfterRunFiles(files: RunnerTestFile[]) {
console.log('finished running', files);
}
}
export default CustomRunner;
WARNING
O Vitest também insere uma instância de ViteNodeRunner
na propriedade __vitest_executor
. Você pode usá-la para processar arquivos no método importFile
(esse é o comportamento padrão para TestRunner
e BenchmarkRunner
).
ViteNodeRunner
expõe o método executeId
, que é usado para importar arquivos de teste em um ambiente amigável ao Vite. Isso significa que ele resolverá as importações e transformará o conteúdo do arquivo em tempo de execução para que o Node possa interpretá-lo:
export default class Runner {
async importFile(filepath: string) {
await this.__vitest_executor.executeId(filepath);
}
}
WARNING
Caso você não tenha um runner personalizado ou não tenha definido o método runTask
, o Vitest tentará obter uma tarefa automaticamente. Se você não adicionou uma função com setFn
, a operação falhará.
TIP
O suporte a snapshots e alguns outros recursos dependem do runner. Para não perdê-los, você pode estender seu runner de VitestTestRunner
, importado de vitest/runners
. Ele também expõe BenchmarkNodeRunner
, caso você deseje estender a funcionalidade de benchmark.
Tarefas
WARNING
A "API de Tarefas do Runner" é experimental e e deve ser usada principalmente no tempo de execução do teste. O Vitest também expõe a "API de Tarefas Reportadas", que deve ser preferida ao trabalhar no thread principal (dentro do reporter, por exemplo).
A equipe está discutindo atualmente se as "Tarefas do Runner" devem ser substituídas pelas "Tarefas Reportadas" no futuro.
Suítes e testes são chamados de tasks
internamente. O runner do Vitest inicia uma tarefa File
antes de coletar qualquer teste – esta é um superconjunto de Suite
com algumas propriedades adicionais. Ela está disponível em cada tarefa (incluindo File
) na propriedade file
.
interface File extends Suite {
/**
* O nome do pool ao qual o arquivo pertence.
* @default 'forks'
*/
pool?: string;
/**
* O caminho para o arquivo no formato Unix.
*/
filepath: string;
/**
* O nome do projeto de teste ao qual o arquivo pertence.
*/
projectName: string | undefined;
/**
* O tempo necessário para coletar todos os testes no arquivo.
* Este tempo também inclui a importação de todas as dependências do arquivo.
*/
collectDuration?: number;
/**
* O tempo que levou para importar o arquivo de configuração.
*/
setupDuration?: number;
}
Cada suíte tem uma propriedade tasks
que é preenchida durante a fase de coleta. É útil para percorrer a árvore de tarefas de cima para baixo.
interface Suite extends TaskBase {
type: 'suite';
/**
* Tarefa de arquivo. É a tarefa raiz do arquivo.
*/
file: File;
/**
* Um array de tarefas que fazem parte da suíte.
*/
tasks: Task[];
}
Cada tarefa tem uma propriedade suite
que referencia a suíte em que está localizada. Se test
ou describe
forem iniciados no nível superior, eles não terão uma propriedade suite
(que não será igual a file
!). File
também nunca tem uma propriedade suite
. É útil para percorrer as tarefas de baixo para cima.
interface Test<ExtraContext = object> extends TaskBase {
type: 'test';
/**
* Contexto de teste que será passado para a função de teste.
*/
context: TestContext & ExtraContext;
/**
* Tarefa de arquivo. É a tarefa raiz do arquivo.
*/
file: File;
/**
* Caso a tarefa tenha sido ignorada chamando `context.skip()`.
*/
pending?: boolean;
/**
* Indica se a tarefa deve ser bem-sucedida mesmo que falhe. Se a tarefa falhar, ela será marcada como passada.
*/
fails?: boolean;
/**
* Armazena promessas (de asserções assíncronas) para aguardá-las antes de concluir o teste
*/
promises?: Promise<any>[];
}
Cada tarefa pode ter um campo result
. As suítes só podem ter este campo se um erro lançado dentro de um callback de suíte ou dos callbacks beforeAll
/afterAll
as impedir de coletar testes. Os testes sempre possuem este campo após a chamada de seus callbacks – os campos state
e errors
estarão presentes dependendo do resultado. Se um erro for lançado nos callbacks beforeEach
ou afterEach
, ele estará presente em task.result.errors
.
export interface TaskResult {
/**
* Estado da tarefa. Herda de `task.mode` durante a coleta.
* Quando a tarefa for concluída, ela será alterada para `pass` ou `fail`.
* - **pass**: tarefa executada com sucesso
* - **fail**: tarefa falhou
*/
state: TaskState;
/**
* Erros que ocorreram durante a execução da tarefa. É possível ter vários erros
* se `expect.soft()` falhou múltiplas vezes.
*/
errors?: ErrorWithDiff[];
/**
* Duração da execução da tarefa em milissegundos.
*/
duration?: number;
/**
* Horário de início da tarefa em milissegundos.
*/
startTime?: number;
/**
* Uso de heap em bytes após a conclusão da tarefa.
* Disponível apenas se a opção `logHeapUsage` e `process.memoryUsage` estiverem definidos.
*/
heap?: number;
/**
* Estado dos hooks relacionados a esta tarefa. Útil para fins de relatório.
*/
hooks?: Partial<
Record<'afterAll' | 'beforeAll' | 'beforeEach' | 'afterEach', TaskState>
>;
/**
* O número de vezes que a tarefa foi reexecutada. A tarefa é reexecutada somente se
* falhou e a opção `retry` estiver definida.
*/
retryCount?: number;
/**
* O número de vezes que a tarefa foi repetida. A tarefa é repetida somente se
* a opção `repeats` estiver definida. Este número inclui também `retryCount`.
*/
repeatCount?: number;
}
Sua Função de Tarefa
O Vitest expõe o utilitário createTaskCollector
para criar seu próprio método test
. Ele se comporta de maneira similar a um teste, mas chama um método personalizado durante a coleta.
Uma tarefa é um objeto que faz parte de uma suíte. É adicionada automaticamente à suíte atual com o método suite.task
:
import { createTaskCollector, getCurrentSuite } from 'vitest/suite';
export { afterAll, beforeAll, describe } from 'vitest';
// esta função será chamada durante a fase de coleta:
// não chame o handler da função aqui, adicione-o às tarefas da suíte
// com o método "getCurrentSuite().task()"
// nota: createTaskCollector fornece suporte para "todo"/"each"/...
export const myCustomTask = createTaskCollector(function (name, fn, timeout) {
getCurrentSuite().task(name, {
...this, // para que "todo"/"skip"/... seja acompanhado corretamente
meta: {
customPropertyToDifferentiateTask: true,
},
handler: fn,
timeout,
});
});
import { afterAll, beforeAll, describe, myCustomTask } from './custom.js';
import { gardener } from './gardener.js';
describe('take care of the garden', () => {
beforeAll(() => {
gardener.putWorkingClothes();
});
myCustomTask('weed the grass', () => {
gardener.weedTheGrass();
});
myCustomTask.todo('mow the lawn', () => {
gardener.mowerTheLawn();
});
myCustomTask('water flowers', () => {
gardener.waterFlowers();
});
afterAll(() => {
gardener.goHome();
});
});
vitest ./garden/tasks.test.js