Skip to content
Vitest 1
Main Navigation GuidaAPIConfigurazioneAvanzato
1.6.1
0.34.6

Italiano

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

Italiano

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

Aspetto

Sidebar Navigation

Guida

Perché Vitest

Introduzione

Funzionalità

Workspace

Interfaccia a riga di comando

Filtro dei test

Reporter

Coverage

Snapshot

Mocking

Test dei Tipi

Interfaccia Utente di Vitest

Modalità Browser

Test nel codice sorgente

Contesto del Test

Ambienti di Test

Estensione dei Matchers

Integrazione con gli IDE

Debugging

Confronti con altri Test Runner

Guida alla Migrazione

Errori Frequenti

Ottimizzare le Prestazioni

API

Riferimento API Test

Funzioni Mock

Vi

expect

expectTypeOf

assert

assertType

Configurazione

Gestione del file di configurazione di Vitest

Configurazione di Vitest

In questa pagina

Mocking ​

Quando si scrivono test, prima o poi si presenta la necessità di creare una versione "falsa" di un servizio interno o esterno. Questo processo è comunemente definito mocking. Vitest fornisce funzioni di utilità per facilitare il mocking tramite il suo helper vi. Puoi import { vi } from 'vitest' o accedervi globalmente (quando la configurazione globale è abilitata).

WARNING

Ricorda sempre di cancellare o ripristinare i mock prima o dopo ogni esecuzione del test per annullare le modifiche allo stato del mock tra le esecuzioni! Consulta la documentazione di mockReset per maggiori informazioni.

Se desideri approfondire subito l'argomento, consulta la sezione API; altrimenti, continua a leggere per esplorare il mondo del mocking.

Date ​

A volte, per garantire la coerenza durante i test, è necessario controllare la data. Vitest utilizza il pacchetto @sinonjs/fake-timers per manipolare i timer, nonché la data di sistema. Puoi trovare ulteriori dettagli sull'API specifica qui.

Esempio ​

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 di utilizzare un tempo simulato
    vi.useFakeTimers();
  });

  afterEach(() => {
    // ripristina la data dopo ogni esecuzione del test
    vi.useRealTimers();
  });

  it('allows purchases within business hours', () => {
    // imposta l'ora all'interno dell'orario di lavoro
    const date = new Date(2000, 1, 1, 13);
    vi.setSystemTime(date);

    // l'accesso a Date.now() restituirà la data impostata sopra
    expect(purchase()).toEqual({ message: 'Success' });
  });

  it('disallows purchases outside of business hours', () => {
    // imposta l'ora al di fuori dell'orario di lavoro
    const date = new Date(2000, 1, 1, 19);
    vi.setSystemTime(date);

    // l'accesso a Date.now() restituirà la data impostata sopra
    expect(purchase()).toEqual({ message: 'Error' });
  });
});

Funzioni ​

Il mocking delle funzioni può essere suddiviso in due categorie distinte: spying (spionaggio) e mocking (simulazione).

A volte è sufficiente verificare se una funzione specifica è stata chiamata o meno (e possibilmente quali argomenti sono stati passati). In questi casi, una spia sarebbe sufficiente, e puoi utilizzarla direttamente con vi.spyOn() (leggi di più qui).

Tuttavia, le spie possono solo aiutarti a monitorare le funzioni; non sono in grado di alterarne l'implementazione. Se invece è necessario creare una versione falsa (o mockata) di una funzione, si può usare vi.fn() (leggi di più qui).

Usiamo Tinyspy come base per il mocking delle funzioni, ma abbiamo il nostro wrapper per renderlo compatibile con jest. Sia vi.fn() che vi.spyOn() condividono gli stessi metodi, tuttavia solo il valore restituito da vi.fn() è richiamabile.

Esempio ​

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);
  });
});

Altro ​

  • Funzioni Mock di Jest

Variabili Globali ​

È possibile mockare variabili globali non presenti in jsdom o node usando l'helper vi.stubGlobal. Questo inserirà il valore della variabile globale in un oggetto 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);

// ora puoi accedervi come `IntersectionObserver` o `window.IntersectionObserver`

Moduli ​

I moduli mockati consentono di intercettare le chiamate a librerie di terze parti nel codice, permettendo di testare argomenti, output o persino di ridefinirne l'implementazione.

Consulta la sezione API vi.mock() per una descrizione API più approfondita e dettagliata.

Algoritmo di Automocking ​

Se il codice importa un modulo mock senza alcun file __mocks__ associato o factory, Vitest mockerà il modulo stesso invocandolo e mockando ogni esportazione.

Si applicano i seguenti principi:

  • Tutti gli array verranno svuotati
  • Tutti i tipi primitivi e le collections rimarranno invariati
  • Tutti gli oggetti verranno clonati in profondità
  • Tutte le istanze delle classi e i loro prototipi verranno clonati in profondità

Moduli Virtuali ​

Vitest supporta il mocking dei moduli virtuali di Vite. Funziona in modo diverso rispetto a come i moduli virtuali vengono trattati in Jest. Invece di passare virtual: true a una funzione vi.mock, devi dire a Vite che il modulo esiste, altrimenti l'analisi fallirà. Puoi farlo in diversi modi:

  1. Fornire un alias
ts
// vitest.config.js
export default {
  test: {
    alias: {
      '$app/forms': resolve('./mocks/forms.js'),
    },
  },
};
  1. Fornire un plugin che risolve un modulo virtuale
ts
// vitest.config.js
export default {
  plugins: [
    {
      name: 'virtual-modules',
      resolveId(id) {
        if (id === '$app/forms') return 'virtual:$app/forms';
      },
    },
  ],
};

Il vantaggio del secondo approccio è che puoi creare dinamicamente diversi punti di ingresso virtuali. Se reindirizzi più moduli virtuali in un singolo file, allora tutti saranno interessati da vi.mock, quindi assicurati di utilizzare identificatori univoci.

Insidie del Mocking ​

Fai attenzione al fatto che non è possibile mockare le chiamate ai metodi che vengono chiamati all'interno di altri metodi dello stesso file. Ad esempio, in questo codice:

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

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

Non è possibile mockare il metodo foo dall'esterno perché viene referenziato direttamente. Quindi questo codice non avrà alcun effetto sulla chiamata foo all'interno di foobar (ma influenzerà la chiamata foo in altri moduli):

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

// this will only affect "foo" outside of the original module
vi.spyOn(mod, 'foo');
vi.mock('./foobar.js', async importOriginal => {
  return {
    ...(await importOriginal<typeof import('./foobar.js')>()),
    // this will only affect "foo" outside of the original module
    foo: () => 'mocked',
  };
});

Puoi confermare questo comportamento fornendo l'implementazione direttamente al metodo foobar:

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

vi.spyOn(mod, 'foo');

// exported foo references mocked method
mod.foobar(mod.foo);
ts
// foobar.js
export function foo() {
  return 'foo';
}

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

Questo è il comportamento previsto. Di solito è segno di codice errato quando il mocking è coinvolto in questo modo. Considera di rifattorizzare il tuo codice in più file o di migliorare l'architettura della tua applicazione utilizzando tecniche come l'iniezione delle dipendenze.

Esempio ​

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 });
  });
});

Richieste ​

Poiché Vitest viene eseguito in Node, mockare le richieste di rete è complesso: le API web non sono disponibili, quindi è necessario qualcosa che ne imiti il comportamento. Si consiglia di utilizzare Mock Service Worker per questo scopo. Questo permette di simulare sia le richieste di rete REST che GraphQL, ed è indipendente dal framework.

Mock Service Worker (MSW) intercetta le richieste effettuate dai test, permettendo di usarlo senza modificare il codice dell'applicazione. Nel browser, MSW utilizza la Service Worker API. In Node.js, e per Vitest, MSW utilizza @mswjs/interceptors. Per saperne di più su MSW, leggi la loro introduzione

Configurazione ​

È possibile utilizzarlo come segue nel file di setup:

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);

// Avvia il server prima di tutti i test
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// Chiudi il server dopo tutti i test
afterAll(() => server.close());

// Reimposta gli handler dopo ogni test (importante per l'isolamento dei test)
afterEach(() => server.resetHandlers());

Configurare il server con onUnhandleRequest: 'error' assicura che venga generato un errore ogni volta che c'è una richiesta che non ha un gestore di richieste corrispondente.

Esempio ​

Ecco un esempio funzionante completo che utilizza MSW: React Testing with MSW.

Altro ​

MSW offre molte altre funzionalità. Puoi gestire i cookie e i parametri di query, definire risposte di errore simulate e molto altro! Per vedere tutto ciò che puoi fare con MSW, leggi la loro documentazione.

Timer ​

Quando si testa codice che coinvolge timeout o intervalli, invece di attendere o far scadere i test, è possibile velocizzarli usando timer "falsi" che simulano le chiamate a setTimeout e setInterval.

Consulta la sezione API vi.useFakeTimers per una descrizione più dettagliata dell'API.

Esempio ​

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);
    // avanzando di 2ms non si attiverà la funzione
    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 negli esempi seguenti viene importato direttamente da vitest. Puoi anche usarlo globalmente, se imposti globals su true nella tua configurazione.

Voglio…

Spiare un metodo ​

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

Simulare variabili esportate ​

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');

Simulare una funzione esportata ​

  1. Esempio con vi.mock:

WARNING

Non dimenticare che una chiamata a vi.mock viene sollevata all'inizio del file. Sarà sempre eseguita prima di tutti gli import.

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

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

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

Simulare l'implementazione di una classe esportata ​

  1. Esempio con 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 conterrà SomeClass
  1. Esempio con vi.mock e valore di ritorno:
ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn(() => ({
    someMethod: vi.fn(),
  }));
  return { SomeClass };
});
// SomeClass.mock.returns conterrà l'oggetto restituito
  1. Esempio con vi.spyOn:
ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'SomeClass').mockImplementation(() => {
  // qualsiasi implementazione dei primi due esempi
});

Spiare un oggetto restituito da una funzione ​

  1. Esempio usando la 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(),
      };
    }
    // ora ogni volta che useObject() viene chiamata,
    // restituirà lo stesso riferimento all'oggetto
    return _cache;
  };
  return { useObject };
});

const obj = useObject();
// obj.method è stata chiamata all'interno di some-path
expect(obj.method).toHaveBeenCalled();

Simulare una parte di un modulo ​

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(); // ha il comportamento originale
mocked(); // è una funzione spia

Simulare la data corrente ​

Per simulare l'ora di Date, puoi usare la funzione helper vi.setSystemTime. Questo valore non verrà automaticamente reimpostato tra test diversi.

Tieni presente che l'utilizzo di vi.useFakeTimers modifica anche l'ora di Date.

ts
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// ripristina l'ora simulata
vi.useRealTimers();

Simulare una variabile globale ​

Puoi impostare una variabile globale assegnando un valore a globalThis o usando l'helper vi.stubGlobal. Quando si utilizza vi.stubGlobal, questo non verrà automaticamente reimpostato tra test diversi, a meno che non si abiliti l'opzione di configurazione unstubGlobals o si chiami vi.unstubAllGlobals.

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

Simulare import.meta.env ​

  1. Per modificare una variabile d'ambiente, è sufficiente assegnarle un nuovo valore.

WARNING

Il valore della variabile d'ambiente non verrà ripristinato automaticamente tra i diversi test.

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

// puoi ripristinarlo manualmente nell'hook beforeEach
const originalViteEnv = import.meta.env.VITE_ENV;

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

it('modifica il valore', () => {
  import.meta.env.VITE_ENV = 'staging';
  expect(import.meta.env.VITE_ENV).toBe('staging');
});
  1. Se desideri reimpostare automaticamente i valori, puoi utilizzare l'helper vi.stubEnv con l'opzione di configurazione unstubEnvs abilitata (oppure chiamare vi.unstubAllEnvs](/api/vi#vi-unstuballenvs) manualmente in un hook beforeEach):
ts
import { expect, it, vi } from 'vitest';

// prima di eseguire i test "VITE_ENV" è "test"
import.meta.env.VITE_ENV === 'test';

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

it('il valore viene ripristinato prima di eseguire un altro test', () => {
  expect(import.meta.env.VITE_ENV).toBe('test');
});
ts
// vitest.config.ts
export default {
  test: {
    unstubAllEnvs: true,
  },
};
Pager
Pagina precedenteSnapshot
Pagina successivaTest dei Tipi

Rilasciato sotto la licenza MIT.

Copyright (c) 2024 Mithril Contributors

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

Rilasciato sotto la licenza MIT.

Copyright (c) 2024 Mithril Contributors