Skip to content
Vitest 1
Main Navigation LeitfadenAPIKonfigurationFortgeschritten
1.6.1
0.34.6

Deutsch

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

Deutsch

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

Aussehen

Sidebar Navigation

Leitfaden

Warum Vitest

Erste Schritte

Features

Arbeitsbereich

Kommandozeilenschnittstelle

Testfilter

Reporter

Codeabdeckung (Coverage)

Snapshot

Mocking

Typen testen

Vitest UI

Browser-Modus

In-Source-Testing

Testkontext

Testumgebung

Erweiterung von Matchern

IDE-Integration

Debugging

Vergleiche mit anderen Test-Runnern

Migrationsleitfaden

Häufige Fehler

Leistungsverbesserung

API

Test API Referenz

Mock-Funktionen

Vi

expect

expectTypeOf

assert

assertType

Konfiguration

Verwaltung der Vitest-Konfigurationsdatei

Vitest konfigurieren

Auf dieser Seite

Mocking ​

Beim Schreiben von Tests dauert es nicht lange, bis du eine simulierte Version eines internen oder externen Dienstes erstellen musst. Dies wird üblicherweise als Mocking bezeichnet. Vitest bietet Hilfsfunktionen, die dir dabei helfen, und zwar über den Helfer vi. Du kannst import { vi } from 'vitest' verwenden oder global darauf zugreifen, wenn die Option globals in der Konfiguration aktiviert ist.

WARNING

Denke immer daran, Mocks vor oder nach jedem Testlauf zurückzusetzen oder wiederherzustellen, um unerwünschte Zustandsänderungen zwischen den Testläufen zu vermeiden! Weitere Informationen findest du in der Dokumentation zu mockReset.

Wenn du direkt loslegen möchtest, schau dir den API-Bereich an. Ansonsten lies weiter, um tiefer in das Thema Mocking einzutauchen.

Dates (Datumsangaben) ​

Manchmal ist es notwendig, das Datum zu manipulieren, um die Konsistenz beim Testen zu gewährleisten. Vitest verwendet das Paket @sinonjs/fake-timers zur Manipulation von Timern und des Systemdatums. Detaillierte Informationen zur spezifischen API findest du hier.

Beispiel ​

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(() => {
    // Vitest mitteilen, dass wir die simulierte Zeit verwenden
    vi.useFakeTimers();
  });

  afterEach(() => {
    // Datum nach jedem Testlauf wiederherstellen
    vi.useRealTimers();
  });

  it('allows purchases within business hours', () => {
    // Stunde innerhalb der Geschäftszeiten setzen
    const date = new Date(2000, 1, 1, 13);
    vi.setSystemTime(date);

    // Der Zugriff auf Date.now() liefert das oben gesetzte Datum
    expect(purchase()).toEqual({ message: 'Success' });
  });

  it('disallows purchases outside of business hours', () => {
    // Stunde außerhalb der Geschäftszeiten setzen
    const date = new Date(2000, 1, 1, 19);
    vi.setSystemTime(date);

    // Der Zugriff auf Date.now() liefert das oben gesetzte Datum
    expect(purchase()).toEqual({ message: 'Error' });
  });
});

Functions (Funktionen) ​

Das Mocking von Funktionen lässt sich in zwei Kategorien unterteilen: Spionage (Spying) & Mocks.

Manchmal musst du nur überprüfen, ob eine bestimmte Funktion aufgerufen wurde (und gegebenenfalls welche Argumente übergeben wurden). In diesen Fällen reicht ein Spy aus, den du direkt mit vi.spyOn() erstellen kannst (mehr dazu hier).

Spies helfen dir jedoch nur, Funktionen zu überwachen. Sie können die Implementierung dieser Funktionen nicht ändern. Wenn du eine gefälschte (oder gemockte) Version einer Funktion erstellen musst, kannst du vi.fn() verwenden (mehr dazu hier).

Wir verwenden Tinyspy als Basis für das Mocking von Funktionen, haben aber einen eigenen Wrapper, um die Kompatibilität mit jest zu gewährleisten. Sowohl vi.fn() als auch vi.spyOn() haben die gleichen Methoden, aber nur das Ergebnis von vi.fn() ist aufrufbar.

Beispiel ​

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, // kann auch ein Getter oder Setter sein, falls unterstützt
};

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

Mehr ​

  • Jest's Mock Functions

Globals ​

Du kannst globale Variablen mocken, die mit jsdom oder node nicht vorhanden sind, indem du den Helfer vi.stubGlobal verwendest. Er setzt den Wert der globalen Variablen im globalThis-Objekt.

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

// now you can access it as `IntersectionObserver` or `window.IntersectionObserver`

Modules (Module) ​

Mock-Module werden verwendet, um Bibliotheken von Drittanbietern zu simulieren, die in deinem Code verwendet werden. Dadurch kannst du Argumente und Ausgaben testen oder sogar die Implementierung der Module ändern.

Weitere Informationen findest du im API-Abschnitt vi.mock() für eine detailliertere API-Beschreibung.

Automocking (Automatisches Mocking) ​

Wenn dein Code ein Modul importiert, für das keine zugehörige __mocks__-Datei oder factory existiert, mockt Vitest das Modul automatisch, indem es es aufruft und jeden Export mockt.

Die folgenden Prinzipien gelten:

  • Alle Arrays werden geleert
  • Alle primitiven Datentypen und Sammlungen bleiben unverändert
  • Alle Objekte werden tief geklont
  • Alle Instanzen von Klassen und ihren Prototypen werden tief geklont

Virtuelle Module ​

Vitest unterstützt das Mocken von Vite virtuellen Modulen. Dies funktioniert anders als die Behandlung virtueller Module in Jest. Anstatt virtual: true an eine vi.mock-Funktion zu übergeben, müssen Sie Vite mitteilen, dass das Modul existiert, da die Analyse sonst fehlschlägt. Dies kann auf verschiedene Arten erfolgen:

  1. Bereitstellung eines Alias
ts
// vitest.config.js
export default {
  test: {
    alias: {
      '$app/forms': resolve('./mocks/forms.js'),
    },
  },
};
  1. Bereitstellung eines Plugins, das ein virtuelles Modul auflöst
ts
// vitest.config.js
export default {
  plugins: [
    {
      name: 'virtual-modules',
      resolveId(id) {
        if (id === '$app/forms') return 'virtual:$app/forms';
      },
    },
  ],
};

Der Vorteil des zweiten Ansatzes ist, dass Sie dynamisch verschiedene virtuelle Einstiegspunkte erstellen können. Wenn Sie mehrere virtuelle Module in eine einzige Datei umleiten, sind alle davon von vi.mock betroffen. Stellen Sie daher sicher, dass Sie eindeutige Kennungen verwenden.

Fallstricke beim Mocken ​

Beachten Sie, dass es nicht möglich ist, Aufrufe von Methoden zu mocken, die innerhalb anderer Methoden derselben Datei aufgerufen werden. Zum Beispiel in diesem Code:

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

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

Es ist nicht möglich, die Methode foo von außen zu mocken, da sie direkt referenziert wird. Dieser Code hat also keine Auswirkungen auf den foo-Aufruf innerhalb von foobar (beeinflusst aber den foo-Aufruf in anderen Modulen):

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

Sie können dieses Verhalten bestätigen, indem Sie die Implementierung direkt an die foobar-Methode übergeben:

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
}

Dies ist das erwartete Verhalten. Es ist normalerweise ein Zeichen für schlechten Code, wenn Mocking auf diese Weise eingesetzt wird. Erwägen Sie, Ihren Code in mehrere Dateien aufzuteilen oder Ihre Anwendungsarchitektur mithilfe von Techniken wie Dependency Injection zu verbessern.

Beispiel ​

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 (Anfragen) ​

Da Vitest in Node ausgeführt wird, ist das Mocking von Netzwerkanfragen schwierig. Web-APIs sind nicht verfügbar, daher benötigen wir etwas, das das Netzwerkverhalten für uns simuliert. Wir empfehlen Mock Service Worker, um dies zu erreichen. Damit kannst du sowohl REST- als auch GraphQL-Netzwerkanfragen mocken, und es ist Framework-unabhängig.

Mock Service Worker (MSW) fängt die Anfragen ab, die deine Tests stellen, sodass du es verwenden kannst, ohne deinen Anwendungscode zu ändern. Im Browser verwendet dies die Service Worker API. In Node.js und für Vitest verwendet es @mswjs/interceptors. Um mehr über MSW zu erfahren, lies ihre Einführung.

Configuration (Konfiguration) ​

Du kannst es wie unten in deiner Setup-Datei verwenden:

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

// Server vor allen Tests starten
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// Server nach allen Tests schließen
afterAll(() => server.close());

// Handler nach jedem Test zurücksetzen (wichtig für die Testisolation)
afterEach(() => server.resetHandlers());

Die Konfiguration des Servers mit onUnhandledRequest: 'error' stellt sicher, dass ein Fehler ausgelöst wird, wenn eine Anfrage vorliegt, für die kein entsprechender Handler definiert ist.

Beispiel ​

Wir haben ein vollständiges, funktionierendes Beispiel, das MSW verwendet: React Testing with MSW.

Mehr ​

Es gibt noch viel mehr zu MSW. Du kannst auf Cookies und Abfrageparameter zugreifen, Mock-Fehlerantworten definieren und vieles mehr! Um alles zu sehen, was du mit MSW machen kannst, lies ihre Dokumentation.

Timers (Timer) ​

Wenn wir Code testen, der Timeouts oder Intervalle verwendet, können wir unsere Tests beschleunigen, indem wir "Fake"-Timer verwenden, die Aufrufe von setTimeout und setInterval simulieren, anstatt auf die tatsächliche Zeit zu warten oder ein Timeout zu riskieren.

Weitere Informationen findest du im API-Abschnitt vi.useFakeTimers für eine detailliertere API-Beschreibung.

Beispiel ​

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);
    // advancing by 2ms won't trigger the func
    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);
  });
});

Kurzübersicht ​

INFO

vi in den Beispielen unten wird direkt von vitest importiert. Du kannst es auch global verwenden, wenn du globals in deiner Konfiguration auf true setzt.

Ziele:

Eine Methode ausspionieren ​

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

Exportierte Variablen simulieren ​

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

Exportierte Funktion simulieren ​

  1. Beispiel mit vi.mock:

WARNING

Vergessen Sie nicht, dass ein vi.mock-Aufruf an den Anfang der Datei verschoben (hoisted) wird. Er wird immer vor allen Importen ausgeführt.

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

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

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

Exportierte Klassenimplementierung simulieren ​

  1. Beispiel mit vi.mock und .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 wird SomeClass enthalten
  1. Beispiel mit vi.mock und Rückgabewert:
ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn(() => ({
    someMethod: vi.fn(),
  }));
  return { SomeClass };
});
// SomeClass.mock.returns wird das zurückgegebene Objekt enthalten
  1. Beispiel mit vi.spyOn:
ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'SomeClass').mockImplementation(() => {
  // was auch immer für deine Zwecke geeignet ist
});

Ein von einer Funktion zurückgegebenes Objekt ausspionieren ​

  1. Beispiel mit Cache-Nutzung:
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(),
      };
    }
    // Dadurch wird bei jedem Aufruf von `useObject()` dieselbe Objektreferenz zurückgegeben.
    return _cache;
  };
  return { useObject };
});

const obj = useObject();
// Die Methode obj.method wurde in some-path aufgerufen
expect(obj.method).toHaveBeenCalled();

Einen Teil eines Moduls simulieren ​

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(); // hat das ursprüngliche Verhalten
mocked(); // ist eine Spy-Funktion

Das aktuelle Datum simulieren ​

Um die Zeit von Date zu simulieren, kannst du die Hilfsfunktion vi.setSystemTime verwenden. Dieser Wert wird nicht automatisch zwischen verschiedenen Tests zurückgesetzt.

Beachte, dass die Verwendung von vi.useFakeTimers ebenfalls die Zeit von Date ändert.

ts
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// Die simulierte Zeit zurücksetzen
vi.useRealTimers();

Eine globale Variable simulieren ​

Du kannst eine globale Variable setzen, indem du globalThis einen Wert zuweist oder den Helfer vi.stubGlobal verwendest. Bei Verwendung von vi.stubGlobal wird dieser Wert nicht automatisch zwischen Tests zurückgesetzt, es sei denn, du aktivierst die Konfigurationsoption unstubGlobals oder rufst vi.unstubAllGlobals auf.

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

import.meta.env simulieren ​

  1. Um eine Umgebungsvariable zu ändern, können Sie ihr einfach einen neuen Wert zuweisen.

WARNING

Der Wert der Umgebungsvariable wird nicht automatisch zwischen verschiedenen Tests zurückgesetzt.

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

// Es kann im beforeEach-Hook manuell zurückgesetzt werden
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. Wenn Sie die Werte automatisch zurücksetzen möchten, können Sie den Hilfsfunktion vi.stubEnv verwenden, wobei die Konfigurationsoption unstubEnvs aktiviert ist (oder vi.unstubAllEnvs](/api/vi#vi-unstuballenvs) manuell in einem beforeEach-Hook aufrufen):
ts
import { expect, it, vi } from 'vitest';

// vor dem Ausführen von Tests ist "VITE_ENV" "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
Vorherige SeiteSnapshot
Nächste SeiteTypen testen

Veröffentlicht unter der MIT-Lizenz.

Copyright (c) 2024 Mithril Contributors

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

Veröffentlicht unter der MIT-Lizenz.

Copyright (c) 2024 Mithril Contributors