Skip to content
Vitest 0
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

Workspace

Kommandozeilenschnittstelle

Testfilter

Codeabdeckung (Coverage)

Snapshot

Mocking

Typentests

Vitest UI

Experimenteller Browser-Modus

In-Source-Testing

Testkontext

Testumgebung

Erweiterung von Matchern

IDE-Integration

Debugging

Vergleiche mit anderen Test-Runnern

Migrationshandbuch

Häufige Fehler

API

Test API Referenz

Mock-Funktionen

Vi

expect

expectTypeOf

assertType

Konfiguration

Konfiguration von Vitest

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.

Dabei gelten folgende Prinzipien:

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

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 node-request-interceptor. 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 { graphql, rest } from 'msw';

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

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

const graphqlHandlers = [
  graphql.query(
    'https://graphql-endpoint.example/api/v1/posts',
    (req, res, ctx) => {
      return res(ctx.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

Beispiel mit vi.mock:

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

vi.mock('./some-path.js', () => ({
  method: vi.fn(),
}));

WARNING

Beachte, dass der vi.mock-Aufruf an den Anfang der Datei verschoben wird. Platziere keine vi.mock-Aufrufe innerhalb von beforeEach. Nur der erste dieser Aufrufe wird ein Modul mocken.

Beispiel mit vi.spyOn:

ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'method').mockImplementation(() => {});
  • Exportierte Klassenimplementierung simulieren

Beispiel mit vi.mock und Prototyp:

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

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

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

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 () => {
  const mod = await vi.importActual<typeof import('./some-path.js')>(
    './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

Um eine Umgebungsvariable zu setzen, kannst du ihr einfach einen neuen Wert zuweisen. Dieser Wert 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');
});

Wenn du den Wert automatisch zurücksetzen möchtest, kannst du den Helfer vi.stubEnv verwenden, wobei die Konfigurationsoption unstubEnvs aktiviert ist (oder vi.unstubAllEnvs manuell im 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 SeiteTypentests

Veröffentlicht unter der MIT-Lizenz.

Copyright (c) 2024 Mithril Contributors

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

Veröffentlicht unter der MIT-Lizenz.

Copyright (c) 2024 Mithril Contributors