Skip to content
Vitest 1
Main Navigation GuiaAPIConfiguraçãoAvançado
1.6.1
0.34.6

Português – Brasil

English
简体中文
繁體中文
Español
Français
Русский
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Português – Brasil

English
简体中文
繁體中文
Español
Français
Русский
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Aparência

Sidebar Navigation

Guia

Por que Vitest

Primeiros Passos

Recursos

Workspace

Interface de Linha de Comando

Filtrando Testes

Reporters

Cobertura

Snapshot

Mocking

Testando Tipos

Vitest UI

Modo Navegador

Testes no Código Fonte

Contexto de Teste

Ambiente de Teste

Expandindo Matchers

Integrações de IDE

Depuração

Comparações com Outros Executores de Teste

Guia de Migração

Erros Comuns

Otimizando o Desempenho

API

Referência da API de Teste

Funções Mock

Vi

expect

expectTypeOf

assert

assertType

Configuração

Gerenciando o Arquivo de Configuração do Vitest

Configurando o Vitest

Nesta página

Mocking ​

Ao escrever testes, é comum precisar criar uma versão "falsa" de um serviço interno ou externo. Isso é chamado de mocking. O Vitest fornece funções utilitárias para auxiliar nesse processo através do helper vi. Você pode import { vi } from 'vitest' ou acessá-lo globalmente (quando a configuração global está habilitada).

WARNING

Lembre-se sempre de limpar ou restaurar os mocks antes ou depois de cada execução de teste para reverter as alterações de estado nos mocks entre as execuções! Consulte a documentação de mockReset para mais informações.

Se você quiser ir direto ao ponto, consulte a seção da API, caso contrário, continue lendo para se aprofundar no mundo do mocking.

Dates ​

Às vezes, é necessário ter controle da data para garantir a consistência ao testar. O Vitest usa o pacote @sinonjs/fake-timers para manipular temporizadores, bem como a data do sistema. Você pode encontrar mais informações sobre a API específica em detalhes aqui.

Example ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const businessHours = [9, 17];

function purchase() {
  const currentHour = new Date().getHours();
  const [open, close] = businessHours;

  if (currentHour > open && currentHour < close) return { message: 'Success' };

  return { message: 'Error' };
}

describe('purchasing flow', () => {
  beforeEach(() => {
    // informa ao Vitest que usaremos tempo simulado
    vi.useFakeTimers();
  });

  afterEach(() => {
    // restaurando a data após cada execução de teste
    vi.useRealTimers();
  });

  it('allows purchases within business hours', () => {
    // define a hora dentro do horário comercial
    const date = new Date(2000, 1, 1, 13);
    vi.setSystemTime(date);

    // acessar Date.now() resultará na data definida acima
    expect(purchase()).toEqual({ message: 'Success' });
  });

  it('disallows purchases outside of business hours', () => {
    // define a hora fora do horário comercial
    const date = new Date(2000, 1, 1, 19);
    vi.setSystemTime(date);

    // acessar Date.now() resultará na data definida acima
    expect(purchase()).toEqual({ message: 'Error' });
  });
});

Functions ​

O Mocking de funções pode ser dividido em duas categorias diferentes: observação (spying) e simulação (mocking).

Às vezes, tudo o que você precisa é validar se uma função específica foi chamada ou não (e possivelmente quais argumentos foram passados). Nesses casos, um spy seria suficiente, e você pode usá-lo diretamente com vi.spyOn() (leia mais aqui).

No entanto, os spies só podem te ajudar a espionar funções, eles não são capazes de alterar a implementação dessas funções. No caso em que precisamos criar uma versão simulada de uma função, podemos usar vi.fn() (leia mais aqui).

Usamos Tinyspy como base para o mocking de funções, mas temos nosso próprio wrapper para torná-lo compatível com jest. Tanto vi.fn() quanto vi.spyOn() compartilham os mesmos métodos, no entanto, apenas o resultado de retorno de vi.fn() é chamável.

Example ​

js
import { afterEach, describe, expect, it, vi } from 'vitest';

function getLatest(index = messages.items.length - 1) {
  return messages.items[index];
}

const messages = {
  items: [
    { message: 'Simple test message', from: 'Testman' },
    // ...
  ],
  getLatest, // can also be a `getter or setter if supported`
};

describe('reading messages', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('should get the latest message with a spy', () => {
    const spy = vi.spyOn(messages, 'getLatest');
    expect(spy.getMockName()).toEqual('getLatest');

    expect(messages.getLatest()).toEqual(
      messages.items[messages.items.length - 1]
    );

    expect(spy).toHaveBeenCalledTimes(1);

    spy.mockImplementationOnce(() => 'access-restricted');
    expect(messages.getLatest()).toEqual('access-restricted');

    expect(spy).toHaveBeenCalledTimes(2);
  });

  it('should get with a mock', () => {
    const mock = vi.fn().mockImplementation(getLatest);

    expect(mock()).toEqual(messages.items[messages.items.length - 1]);
    expect(mock).toHaveBeenCalledTimes(1);

    mock.mockImplementationOnce(() => 'access-restricted');
    expect(mock()).toEqual('access-restricted');

    expect(mock).toHaveBeenCalledTimes(2);

    expect(mock()).toEqual(messages.items[messages.items.length - 1]);
    expect(mock).toHaveBeenCalledTimes(3);
  });
});

More ​

  • Jest's Mock Functions

Globals ​

Você pode simular variáveis globais que não estão presentes com jsdom ou node usando o auxiliar vi.stubGlobal. Ele definirá o valor da variável global em um objeto globalThis.

ts
import { vi } from 'vitest';

const IntersectionObserverMock = vi.fn(() => ({
  disconnect: vi.fn(),
  observe: vi.fn(),
  takeRecords: vi.fn(),
  unobserve: vi.fn(),
}));

vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);

// agora você pode acessá-lo como `IntersectionObserver` ou `window.IntersectionObserver`

Modules ​

Módulos mock simulam bibliotecas de terceiros invocadas no código, permitindo testar argumentos, saídas e até mesmo redefinir suas implementações.

Veja a seção da API vi.mock() para uma descrição da API mais detalhada e aprofundada.

Automocking algorithm ​

Se o seu código importar um módulo simulado, sem um arquivo __mocks__ ou factory associado, o Vitest simulará o módulo invocando-o e simulando cada exportação.

Os seguintes princípios se aplicam:

  • Todos os arrays serão esvaziados.
  • Todos os tipos primitivos e coleções permanecerão os mesmos.
  • Todos os objetos serão clonados profundamente.
  • Todas as instâncias de classes e seus protótipos serão clonadas profundamente.

Módulos Virtuais ​

O Vitest oferece suporte a simulação de módulos virtuais do Vite. Ele funciona de forma diferente de como os módulos virtuais são tratados no Jest. Em vez de passar virtual: true para uma função vi.mock, você precisa informar ao Vite que o módulo existe, caso contrário, ele falhará durante a análise. Você pode fazer isso de várias maneiras:

  1. Forneça um alias
ts
// vitest.config.js
export default {
  test: {
    alias: {
      '$app/forms': resolve('./mocks/forms.js'),
    },
  },
};
  1. Forneça um plugin que resolva um módulo virtual
ts
// vitest.config.js
export default {
  plugins: [
    {
      name: 'virtual-modules',
      resolveId(id) {
        if (id === '$app/forms') return 'virtual:$app/forms';
      },
    },
  ],
};

O benefício da segunda abordagem é que você pode criar dinamicamente diferentes pontos de entrada virtuais. Se você redirecionar vários módulos virtuais para um único arquivo, todos eles serão afetados por vi.mock, portanto, certifique-se de usar identificadores exclusivos.

Armadilhas de Simulação ​

Esteja ciente de que não é possível simular chamadas para métodos que são chamados dentro de outros métodos do mesmo arquivo. Por exemplo, neste código:

ts
export function foo() {
  return 'foo';
}

export function foobar() {
  return `${foo()}bar`;
}

Não é possível simular o método foo de fora porque ele é referenciado diretamente. Portanto, este código não terá efeito na chamada foo dentro de foobar (mas afetará a chamada foo em outros módulos):

ts
import { vi } from 'vitest';
import * as mod from './foobar.js';

// isso só afetará "foo" fora do módulo original
vi.spyOn(mod, 'foo');
vi.mock('./foobar.js', async importOriginal => {
  return {
    ...(await importOriginal<typeof import('./foobar.js')>()),
    // isso só afetará "foo" fora do módulo original
    foo: () => 'mocked',
  };
});

Você pode confirmar esse comportamento fornecendo a implementação ao método foobar diretamente:

ts
// foobar.test.js
import * as mod from './foobar.js';

vi.spyOn(mod, 'foo');

// foo exportado faz referência ao método simulado
mod.foobar(mod.foo);
ts
// foobar.js
export function foo() {
  return 'foo';
}

export function foobar(injectedFoo) {
  return injectedFoo !== foo; // false
}

Este é o comportamento pretendido. Geralmente, é um sinal de código ruim quando a simulação está envolvida de tal maneira. Considere refatorar seu código em vários arquivos ou melhorar a arquitetura do seu aplicativo usando técnicas como injeção de dependência.

Example ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { Client } from 'pg';
import { failure, success } from './handlers.js';

// handlers
export function success(data) {}
export function failure(data) {}

// get todos
export async function getTodos(event, context) {
  const client = new Client({
    // ...clientOptions
  });

  await client.connect();

  try {
    const result = await client.query('SELECT * FROM todos;');

    client.end();

    return success({
      message: `${result.rowCount} item(s) returned`,
      data: result.rows,
      status: true,
    });
  } catch (e) {
    console.error(e.stack);

    client.end();

    return failure({ message: e, status: false });
  }
}

vi.mock('pg', () => {
  const Client = vi.fn();
  Client.prototype.connect = vi.fn();
  Client.prototype.query = vi.fn();
  Client.prototype.end = vi.fn();

  return { Client };
});

vi.mock('./handlers.js', () => {
  return {
    success: vi.fn(),
    failure: vi.fn(),
  };
});

describe('get a list of todo items', () => {
  let client;

  beforeEach(() => {
    client = new Client();
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  it('should return items successfully', async () => {
    client.query.mockResolvedValueOnce({ rows: [], rowCount: 0 });

    await getTodos();

    expect(client.connect).toBeCalledTimes(1);
    expect(client.query).toBeCalledWith('SELECT * FROM todos;');
    expect(client.end).toBeCalledTimes(1);

    expect(success).toBeCalledWith({
      message: '0 item(s) returned',
      data: [],
      status: true,
    });
  });

  it('should throw an error', async () => {
    const mError = new Error('Unable to retrieve rows');
    client.query.mockRejectedValueOnce(mError);

    await getTodos();

    expect(client.connect).toBeCalledTimes(1);
    expect(client.query).toBeCalledWith('SELECT * FROM todos;');
    expect(client.end).toBeCalledTimes(1);
    expect(failure).toBeCalledWith({ message: mError, status: false });
  });
});

Requests ​

Como o Vitest é executado no Node, simular requisições de rede é complicado; as APIs da web não estão disponíveis, então precisamos de algo que imite o comportamento da rede para nós. Recomendamos o Mock Service Worker para realizar isso. Ele permitirá que você simule requisições de rede REST e GraphQL, e é independente de framework.

O Mock Service Worker (MSW) funciona interceptando as requisições que seus testes fazem, permitindo que você o use sem alterar nenhum código do seu aplicativo. No navegador, isso usa a Service Worker API. No Node.js, e para o Vitest, ele usa @mswjs/interceptors. Para saber mais sobre o MSW, leia a introdução deles.

Configuration ​

Você pode usá-lo como abaixo no seu arquivo de configuração:

js
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { HttpResponse, graphql, http } from 'msw';

const posts = [
  {
    userId: 1,
    id: 1,
    title: 'first post title',
    body: 'first post body',
  },
  // ...
];

export const restHandlers = [
  http.get('https://rest-endpoint.example/path/to/posts', () => {
    return HttpResponse.json(posts);
  }),
];

const graphqlHandlers = [
  graphql.query('ListPosts', () => {
    return HttpResponse.json({
      data: { posts },
    });
  }),
];

const server = setupServer(...restHandlers, ...graphqlHandlers);

// Inicia o servidor antes de todos os testes
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// Fecha o servidor após todos os testes
afterAll(() => server.close());

// Reseta os handlers após cada teste (importante para o isolamento dos testes)
afterEach(() => server.resetHandlers());

Configurar o servidor com onUnhandledRequest: 'error' garante que um erro seja lançado sempre que houver uma requisição que não tenha um manipulador de requisição correspondente.

Example ​

Temos um exemplo completo e funcional que usa MSW: React Testing with MSW.

More ​

Há muito mais no MSW. Você pode acessar cookies e parâmetros de consulta, definir respostas de erro mockadas e muito mais! Para ver tudo o que você pode fazer com o MSW, leia a documentação deles.

Timers ​

Quando testamos código com timeouts ou intervals, em vez de esperar ou atingir o tempo limite, podemos acelerar os testes usando timers "falsos" que simulam chamadas para setTimeout e setInterval.

Veja a seção da API vi.useFakeTimers para uma descrição da API mais detalhada e aprofundada.

Example ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

function executeAfterTwoHours(func) {
  setTimeout(func, 1000 * 60 * 60 * 2); // 2 hours
}

function executeEveryMinute(func) {
  setInterval(func, 1000 * 60); // 1 minute
}

const mock = vi.fn(() => console.log('executed'));

describe('delayed execution', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });
  afterEach(() => {
    vi.restoreAllMocks();
  });
  it('should execute the function', () => {
    executeAfterTwoHours(mock);
    vi.runAllTimers();
    expect(mock).toHaveBeenCalledTimes(1);
  });
  it('should not execute the function', () => {
    executeAfterTwoHours(mock);
    // avançando em 2ms não irá disparar a função
    vi.advanceTimersByTime(2);
    expect(mock).not.toHaveBeenCalled();
  });
  it('should execute every minute', () => {
    executeEveryMinute(mock);
    vi.advanceTimersToNextTimer();
    expect(mock).toHaveBeenCalledTimes(1);
    vi.advanceTimersToNextTimer();
    expect(mock).toHaveBeenCalledTimes(2);
  });
});

Cheat Sheet ​

INFO

vi nos exemplos abaixo é importado diretamente de vitest. Você também pode usá-lo globalmente, se definir globals como true em sua configuração.

Eu quero…

Espionar um method ​

ts
const instance = new SomeClass();
vi.spyOn(instance, 'method');

Simular variáveis exportadas ​

js
// some-path.js
export const getter = 'variable';
ts
// some-path.test.ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked');

Simular função exportada ​

  1. Exemplo com vi.mock:

WARNING

Não se esqueça que uma chamada vi.mock é elevada para o topo do arquivo. Ela sempre será executada antes de todas as importações.

ts
// ./some-path.js
export function method() {}
ts
import { method } from './some-path.js';

vi.mock('./some-path.js', () => ({
  method: vi.fn(),
}));
  1. Exemplo com vi.spyOn:
ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'method').mockImplementation(() => {});

Simular a implementação de uma classe exportada ​

  1. Exemplo com vi.mock e .prototype:
ts
// some-path.ts
export class SomeClass {}
ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn();
  SomeClass.prototype.someMethod = vi.fn();
  return { SomeClass };
});
// SomeClass.mock.instances terá SomeClass
  1. Exemplo com vi.mock e valor de retorno:
ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn(() => ({
    someMethod: vi.fn(),
  }));
  return { SomeClass };
});
// SomeClass.mock.returns terá o objeto retornado
  1. Exemplo com vi.spyOn:
ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'SomeClass').mockImplementation(() => {
  // qualquer implementação que você preferir dos exemplos anteriores
});

Espionar um objeto retornado por uma função ​

  1. Exemplo usando cache:
ts
// some-path.ts
export function useObject() {
  return { method: () => true };
}
ts
// useObject.js
import { useObject } from './some-path.js';

const obj = useObject();
obj.method();
ts
// useObject.test.js
import { useObject } from './some-path.js';

vi.mock('./some-path.js', () => {
  let _cache;
  const useObject = () => {
    if (!_cache) {
      _cache = {
        method: vi.fn(),
      };
    }
    // agora, toda vez que useObject() for chamado, retornará a mesma referência de objeto
    return _cache;
  };
  return { useObject };
});

const obj = useObject();
// obj.method foi chamado dentro de some-path
expect(obj.method).toHaveBeenCalled();

Simular uma parte de um módulo ​

ts
import { mocked, original } from './some-path.js';

vi.mock('./some-path.js', async importOriginal => {
  const mod = await importOriginal<typeof import('./some-path.js')>();
  return {
    ...mod,
    mocked: vi.fn(),
  };
});
original(); // has original behaviour
mocked(); // is a spy function

Simular a data atual ​

Para simular o tempo de Date, você pode usar a função auxiliar vi.setSystemTime. Este valor não será automaticamente redefinido entre diferentes testes.

Esteja ciente de que usar vi.useFakeTimers também altera o tempo de Date.

ts
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// reset mocked time
vi.useRealTimers();

Simular uma variável global ​

Você pode definir uma variável global atribuindo um valor a globalThis ou usando o auxiliar vi.stubGlobal. Ao usar vi.stubGlobal, ele não será automaticamente redefinido entre diferentes testes, a menos que você habilite a opção de configuração unstubGlobals ou chame vi.unstubAllGlobals.

ts
vi.stubGlobal('__VERSION__', '1.0.0');
expect(__VERSION__).toBe('1.0.0');

Simular import.meta.env ​

  1. Para alterar uma variável de ambiente, você pode simplesmente atribuir um novo valor a ela.

WARNING

O valor da variável de ambiente não será redefinido (reset) automaticamente entre os diferentes testes.

ts
import { beforeEach, expect, it } from 'vitest';

// you can reset it in beforeEach hook manually
const originalViteEnv = import.meta.env.VITE_ENV;

beforeEach(() => {
  import.meta.env.VITE_ENV = originalViteEnv;
});

it('changes value', () => {
  import.meta.env.VITE_ENV = 'staging';
  expect(import.meta.env.VITE_ENV).toBe('staging');
});
  1. Se você deseja redefinir os valores automaticamente, pode usar o auxiliar vi.stubEnv com a opção de configuração unstubEnvs habilitada (ou chamar vi.unstubAllEnvs](/api/vi#vi-unstuballenvs) manualmente em um hook beforeEach):
ts
import { expect, it, vi } from 'vitest';

// before running tests "VITE_ENV" is "test"
import.meta.env.VITE_ENV === 'test';

it('changes value', () => {
  vi.stubEnv('VITE_ENV', 'staging');
  expect(import.meta.env.VITE_ENV).toBe('staging');
});

it('the value is restored before running an other test', () => {
  expect(import.meta.env.VITE_ENV).toBe('test');
});
ts
// vitest.config.ts
export default {
  test: {
    unstubAllEnvs: true,
  },
};
Pager
AnteriorSnapshot
PróximoTestando Tipos

Distribuído sob a Licença MIT.

Copyright (c) 2024 Mithril Contributors

https://v1.vitest.dev/guide/mocking

Distribuído sob a Licença MIT.

Copyright (c) 2024 Mithril Contributors