Skip to content
Vitest 1
Main Navigation GuíaAPIConfiguraciónAvanzado
1.6.1
0.34.6

Español

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

Español

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

Apariencia

Sidebar Navigation

Guía

Por qué Vitest

Empezando

Características

Área de Trabajo

Interfaz de Línea de Comandos

Filtrado de Pruebas

Informes

Cobertura

Capturas instantáneas

Mocking

Pruebas de Tipos

Interfaz de Usuario de Vitest

Modo Navegador

Pruebas en el código fuente

Contexto de prueba

Entorno de Pruebas

Extender Matchers

Integración con IDEs

Depuración

Comparaciones con otros Ejecutores de Pruebas

Guía de Migración

Errores frecuentes

Mejora del rendimiento

API

Referencia de la API de pruebas

Funciones Mock

Vi

expect

expectTypeOf

assert

assertType

Configuración

Administración del archivo de configuración de Vitest

Configuración de Vitest

En esta página

Mocking ​

Al escribir pruebas, es inevitable que necesites crear una versión "falsa" de un servicio, ya sea interno o externo. Esto se conoce comúnmente como mocking (simulación). Vitest proporciona funciones de utilidad para ayudarte a través de su helper vi. Puedes import { vi } from 'vitest' o acceder a él globalmente (cuando la configuración global está habilitada).

WARNING

¡Recuerda siempre limpiar o restaurar los mocks antes o después de cada ejecución de prueba para deshacer los cambios de estado de los mocks entre ejecuciones! Consulta la documentación de mockReset para obtener más información.

Si quieres empezar directamente, consulta la sección de la API; si no, sigue leyendo para profundizar en el mundo del mocking.

Dates (Fechas) ​

A veces, necesitas tener el control de la fecha para garantizar la coherencia en las pruebas. Vitest utiliza el paquete @sinonjs/fake-timers para manipular los temporizadores, así como la fecha del sistema. Puedes encontrar información más detallada sobre la API específica aquí.

Example (Ejemplo) ​

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(() => {
    // indica a vitest que usaremos tiempo simulado
    vi.useFakeTimers();
  });

  afterEach(() => {
    // restaurando la fecha después de cada ejecución de prueba
    vi.useRealTimers();
  });

  it('allows purchases within business hours', () => {
    // establece la hora dentro del horario comercial
    const date = new Date(2000, 1, 1, 13);
    vi.setSystemTime(date);

    // acceder a Date.now() resultará en la fecha establecida arriba
    expect(purchase()).toEqual({ message: 'Success' });
  });

  it('disallows purchases outside of business hours', () => {
    // establece la hora fuera del horario comercial
    const date = new Date(2000, 1, 1, 19);
    vi.setSystemTime(date);

    // acceder a Date.now() resultará en la fecha establecida arriba
    expect(purchase()).toEqual({ message: 'Error' });
  });
});

Functions (Funciones) ​

El mocking de funciones se puede dividir en dos categorías diferentes: spying (espionaje) & mocking (simulación).

A veces, solo necesitas validar si una función específica ha sido llamada o no (y posiblemente qué argumentos se pasaron). En estos casos, un spy sería suficiente, y puedes usarlo directamente con vi.spyOn() (lee más aquí).

Sin embargo, los spies solo pueden ayudarte a espiar funciones, no pueden modificar su implementación. En el caso de que necesitemos crear una versión falsa (o simulada) de una función, podemos usar vi.fn() (lee más aquí).

Usamos Tinyspy como base para el mocking de funciones, pero tenemos nuestro propio wrapper para hacerlo compatible con jest. Tanto vi.fn() como vi.spyOn() comparten los mismos métodos; sin embargo, solo el resultado de retorno de vi.fn() es invocable.

Example (Ejemplo) ​

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 (Más) ​

  • Jest's Mock Functions

Globals (Variables Globales) ​

Puedes simular variables globales que no están presentes con jsdom o node utilizando el helper vi.stubGlobal. Esto asignará el valor de la variable global a un 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);

// ahora puedes acceder a ella como `IntersectionObserver` o `window.IntersectionObserver`

Modules (Módulos) ​

Los módulos simulados se utilizan para simular bibliotecas de terceros que se invocan en otro código, lo que te permite probar argumentos, la salida o incluso redeclarar su implementación.

Consulta la sección de la API vi.mock() para obtener una descripción más detallada de la API.

Automocking algorithm (Algoritmo de Automocking) ​

Si tu código está importando un módulo simulado, sin ningún archivo __mocks__ asociado o factory para este módulo, Vitest simulará el módulo en sí invocándolo y simulando cada exportación.

Se aplican los siguientes principios:

  • Todos los arrays se vaciarán
  • Todos los tipos de datos primitivos y colecciones permanecerán igual
  • Todos los objetos se clonarán profundamente
  • Todas las instancias de clases y sus prototipos se clonarán profundamente

Módulos Virtuales ​

Vitest admite la simulación de módulos virtuales de Vite. Funciona de manera diferente a cómo se tratan los módulos virtuales en Jest. En lugar de pasar virtual: true a una función vi.mock, debes indicarle a Vite que el módulo existe; de lo contrario, fallará durante el análisis. Puedes hacerlo de varias maneras:

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

El beneficio del segundo enfoque es que puedes crear dinámicamente diferentes puntos de entrada virtuales. Si rediriges varios módulos virtuales a un solo archivo, todos se verán afectados por vi.mock, así que asegúrate de utilizar identificadores únicos.

Trampas de la simulación ​

Ten cuidado porque no es posible simular llamadas a métodos que se llaman dentro de otros métodos del mismo archivo. Por ejemplo, en este código:

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

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

No es posible simular el método foo desde fuera porque se hace referencia directamente. Por lo tanto, este código no tendrá ningún efecto en la llamada a foo dentro de foobar (pero afectará a la llamada a foo en otros módulos):

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

// esto solo afectará a "foo" fuera del módulo original
vi.spyOn(mod, 'foo');
vi.mock('./foobar.js', async importOriginal => {
  return {
    ...(await importOriginal<typeof import('./foobar.js')>()),
    // esto solo afectará a "foo" fuera del módulo original
    foo: () => 'mocked',
  };
});

Puedes confirmar este comportamiento proporcionando la implementación al método foobar directamente:

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

vi.spyOn(mod, 'foo');

// foo exportado hace referencia al método simulado
mod.foobar(mod.foo);
ts
// foobar.js
export function foo() {
  return 'foo';
}

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

Este es el comportamiento esperado. Por lo general, es una señal de código incorrecto cuando la simulación está involucrada de tal manera. Considera refactorizar tu código en varios archivos o mejorar la arquitectura de tu aplicación utilizando técnicas como la inyección de dependencias.

Example (Ejemplo) ​

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

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

// get todos (obtener tareas)
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 (Peticiones) ​

Dado que Vitest se ejecuta en Node, simular peticiones de red es complicado; las APIs web no están disponibles, por lo que necesitamos algo que simule el comportamiento de la red. Recomendamos Mock Service Worker para lograr esto. Te permitirá simular peticiones de red tanto REST como GraphQL, y es independiente del framework.

Mock Service Worker (MSW) funciona interceptando las peticiones que hacen tus pruebas, lo que te permite usarlo sin cambiar nada de tu código de aplicación. En el navegador, esto usa la Service Worker API. En Node.js, y para Vitest, usa @mswjs/interceptors. Para aprender más sobre MSW, lee su introducción

Configuration (Configuración) ​

Puedes usarlo como se muestra a continuación en tu archivo de configuración

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 el servidor antes de todas las pruebas
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// Cierra el servidor después de todas las pruebas
afterAll(() => server.close());

// Restablece los handlers después de cada prueba (importante para el aislamiento de las pruebas)
afterEach(() => server.resetHandlers());

Configurar el servidor con onUnhandleRequest: 'error' garantiza que se genere un error cuando haya una petición sin un handler correspondiente.

Example (Ejemplo) ​

Tenemos un ejemplo completo que usa MSW: React Testing with MSW.

More (Más) ​

Hay mucho más en MSW. Puedes acceder a cookies y parámetros de consulta, definir respuestas de error simuladas, ¡y mucho más! Para ver todo lo que puedes hacer con MSW, lee su documentación.

Timers (Temporizadores) ​

Cuando probamos código que involucra timeouts o intervals, en lugar de hacer que nuestras pruebas esperen o excedan el tiempo de espera, podemos acelerar nuestras pruebas usando temporizadores "falsos" que simulan llamadas a setTimeout y setInterval.

Consulta la sección de la API vi.useFakeTimers para obtener una descripción más detallada de la API.

Example (Ejemplo) ​

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);
    // avanzar 2ms no activará la función
    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);
  });
});

Hoja de Referencia ​

INFO

vi en los ejemplos siguientes se importa directamente desde vitest. También puedes usarlo globalmente si estableces globals a true en tu configuración.

Quiero…

Espiar un método ​

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

Simular variables 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 una función exportada ​

  1. Ejemplo con vi.mock:

WARNING

No olvides que una llamada a vi.mock se eleva a la parte superior del archivo. Siempre se ejecutará antes de todas las importaciones.

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

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

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

Simular la implementación de una clase exportada ​

  1. Ejemplo con vi.mock y .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 contendrá instancias de SomeClass
  1. Ejemplo con vi.mock y 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 contendrá el objeto retornado
  1. Ejemplo con vi.spyOn:
ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'SomeClass').mockImplementation(() => {
  // lo que necesites de los dos primeros ejemplos
});

Espiar un objeto devuelto por una función ​

  1. Ejemplo usando caché:
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(),
      };
    }
    // ahora cada vez que se llama a useObject()
    // devolverá la misma referencia al objeto
    return _cache;
  };
  return { useObject };
});

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

Simular parte de un 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(); // tiene el comportamiento original
mocked(); // es una función espía

Simular la fecha actual ​

Para simular el tiempo de Date, puedes usar la función auxiliar vi.setSystemTime. Este valor no se restablecerá automáticamente entre pruebas.

Ten en cuenta que usar vi.useFakeTimers también modifica el tiempo de Date.

ts
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// restablecer el tiempo simulado
vi.useRealTimers();

Simular una variable global ​

Puedes establecer una variable global asignando un valor a globalThis o usando el auxiliar vi.stubGlobal. Cuando se usa vi.stubGlobal, no se restablecerá automáticamente entre pruebas, a menos que actives la opción de configuración unstubGlobals o llames a vi.unstubAllGlobals.

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

Simular import.meta.env ​

  1. Para cambiar una variable de entorno, simplemente puedes asignarle un valor nuevo.

WARNING

El valor de la variable de entorno no se restablecerá automáticamente entre diferentes pruebas.

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

// puedes restablecerlo manualmente en el hook beforeEach
const originalViteEnv = import.meta.env.VITE_ENV;

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

it('cambia el valor', () => {
  import.meta.env.VITE_ENV = 'staging';
  expect(import.meta.env.VITE_ENV).toBe('staging');
});
  1. Si deseas restablecer los valores automáticamente, puedes usar el auxiliar vi.stubEnv con la opción de configuración unstubEnvs habilitada (o llamar a vi.unstubAllEnvs](/api/vi#vi-unstuballenvs) manualmente en un hook beforeEach):
ts
import { expect, it, vi } from 'vitest';

// antes de ejecutar las pruebas "VITE_ENV" es "test"
import.meta.env.VITE_ENV === 'test';

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

it('el valor se restaura antes de ejecutar otra prueba', () => {
  expect(import.meta.env.VITE_ENV).toBe('test');
});
ts
// vitest.config.ts
export default {
  test: {
    unstubAllEnvs: true,
  },
};
Pager
AnteriorCapturas instantáneas
SiguientePruebas de Tipos

Publicado bajo la licencia MIT.

Copyright (c) 2024 Mithril Contributors

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

Publicado bajo la licencia MIT.

Copyright (c) 2024 Mithril Contributors