Skip to content
Vitest 1
Main Navigation PrůvodceAPIKonfiguracePokročilý
1.6.1
0.34.6

čeština

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

čeština

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

Vzhled

Sidebar Navigation

Průvodce

Proč Vitest

Začínáme

Funkce

Workspace

Rozhraní příkazového řádku

Filtrování testů

Reportéři

Pokrytí

Snímky

Mockování

Testování typů

Vitest UI

Režim prohlížeče

Testování ve zdrojovém kódu

Kontext testu

Testovací prostředí

Rozšíření matcherů/porovnávačů

Integrace do IDE

Ladění

Srovnání s jinými testovacími nástroji

Průvodce migrací

Běžné chyby

Zvýšení výkonu

API

Referenční příručka k Test API

Mock funkce

Vi

expect

expectTypeOf

assert

assertType

Konfigurace

Správa konfiguračního souboru pro Vitest

Konfigurace Vitest

Na této stránce

Mockování ​

Při psaní testů dříve či později narazíte na potřebu vytvořit "falešnou" verzi interní nebo externí služby. Tomu se obvykle říká mockování. Vitest poskytuje pomocné funkce, které vám s tím pomohou, prostřednictvím svého pomocníka vi. Můžete import { vi } from 'vitest' nebo k němu přistupovat globálně (pokud je globální konfigurace povolena).

WARNING

Nezapomeňte vždy smazat nebo obnovit mocky před nebo po každém spuštění testu, abyste zrušili změny stavu mocků mezi spuštěními! Další informace naleznete v dokumentaci mockReset.

Pokud se chcete rovnou pustit do práce, podívejte se na sekci API. Jinak pokračujte ve čtení a prozkoumejte svět mockování podrobněji.

Datum a čas ​

Někdy potřebujete mít kontrolu nad časem, abyste zajistili konzistenci během testování. Vitest používá balíček @sinonjs/fake-timers pro manipulaci s časovači a systémovým datem. Více informací o konkrétním API naleznete zde.

Příklad ​

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(() => {
    // informujeme Vitest, že používáme mockovaný čas
    vi.useFakeTimers();
  });

  afterEach(() => {
    // obnovení data po každém spuštění testu
    vi.useRealTimers();
  });

  it('allows purchases within business hours', () => {
    // nastavíme hodinu v rámci pracovní doby
    const date = new Date(2000, 1, 1, 13);
    vi.setSystemTime(date);

    // přístup k Date.now() vrátí výše nastavené datum
    expect(purchase()).toEqual({ message: 'Success' });
  });

  it('disallows purchases outside of business hours', () => {
    // nastavíme hodinu mimo pracovní dobu
    const date = new Date(2000, 1, 1, 19);
    vi.setSystemTime(date);

    // přístup k Date.now() vrátí výše nastavené datum
    expect(purchase()).toEqual({ message: 'Error' });
  });
});

Funkce ​

Mockování funkcí lze rozdělit do dvou kategorií: spying & mocking (sledování a napodobování).

Někdy potřebujete pouze ověřit, zda byla konkrétní funkce volána (a případně s jakými argumenty). V těchto případech by nám stačilo špehování, které můžete použít přímo s vi.spyOn() (více informací zde).

Špehovací funkce vám umožňují pouze sledovat chování funkcí, nikoli měnit jejich implementaci. V případě, že potřebujeme vytvořit falešnou verzi funkce (nebo mockovanou), můžeme použít vi.fn() (více informací zde).

Používáme Tinyspy jako základ pro mockování funkcí, ale máme vlastní obal, aby byl kompatibilní s jest. Jak vi.fn(), tak vi.spyOn() sdílejí stejné metody, nicméně pouze návratová hodnota vi.fn() je volatelná.

Příklad ​

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

Více ​

  • Jest's Mock Functions

Globální objekty ​

Globální proměnné, které nejsou přítomny v jsdom nebo node, můžete mockovat pomocí pomocníka vi.stubGlobal. Vloží hodnotu globální proměnné do objektu 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);

// nyní k němu můžete přistupovat jako `IntersectionObserver` nebo `window.IntersectionObserver`

Moduly ​

Mockování modulů umožňuje sledovat chování knihoven třetích stran volaných ve vašem kódu, testovat jejich argumenty a výstupy a dokonce i měnit jejich implementaci.

Podrobnější popis API naleznete v sekci vi.mock() api.

Automatické mockování ​

Pokud váš kód importuje mockovaný modul bez jakéhokoli přidruženého souboru __mocks__ nebo factory pro tento modul, Vitest bude mockovat samotný modul jeho vyvoláním a mockováním každého exportu.

Platí následující principy:

  • Všechna pole budou vyprázdněna
  • Všechny primitivy a kolekce zůstanou stejné
  • Všechny objekty budou hluboce naklonovány
  • Všechny instance tříd a jejich prototypy budou hluboce naklonovány

Virtuální moduly ​

Vitest podporuje mockování Vite virtuálních modulů. Funguje to jinak než u virtuálních modulů v Jestu. Místo předávání virtual: true do funkce vi.mock musíte říct Vite, že modul existuje, jinak se to při parsování nezdaří. Můžete to udělat několika způsoby:

  1. Poskytnutí aliasu
ts
// vitest.config.js
export default {
  test: {
    alias: {
      '$app/forms': resolve('./mocks/forms.js'),
    },
  },
};
  1. Poskytnutí pluginu, který vyřeší (resolve) virtuální modul
ts
// vitest.config.js
export default {
  plugins: [
    {
      name: 'virtual-modules',
      resolveId(id) {
        if (id === '$app/forms') return 'virtual:$app/forms';
      },
    },
  ],
};

Výhodou druhého přístupu je, že můžete dynamicky vytvářet různé virtuální vstupní body. Pokud přesměrujete několik virtuálních modulů do jednoho souboru, budou všechny ovlivněny funkcí vi.mock, takže se ujistěte, že používáte jedinečné identifikátory.

Úskalí mockování ​

Mějte na paměti, že není možné mockovat volání metod, které jsou volány uvnitř jiných metod ve stejném souboru. Například v tomto kódu:

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

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

Není možné mockovat metodu foo zvenčí, protože je na ni přímo odkazováno. Takže tento kód nebude mít žádný vliv na volání foo uvnitř foobar (ale ovlivní volání foo v jiných modulech):

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

// toto ovlivní pouze "foo" mimo původní modul
vi.spyOn(mod, 'foo');
vi.mock('./foobar.js', async importOriginal => {
  return {
    ...(await importOriginal<typeof import('./foobar.js')>()),
    // toto ovlivní pouze "foo" mimo původní modul
    foo: () => 'mocked',
  };
});

Toto chování si můžete ověřit přímým poskytnutím implementace metodě foobar:

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

vi.spyOn(mod, 'foo');

// exportované foo odkazuje na mockovanou metodu
mod.foobar(mod.foo);
ts
// foobar.js
export function foo() {
  return 'foo';
}

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

Toto je zamýšlené chování. Obvykle je to známka špatného kódu, pokud je mockování zapojeno takovým způsobem. Zvažte refaktorování kódu do více souborů nebo vylepšení architektury vaší aplikace pomocí technik, jako je vkládání závislostí.

Příklad ​

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

// Obslužné funkce
export function success(data) {}
export function failure(data) {}

// Získání úkolů
export async function getTodos(event, context) {
  const client = new Client({
    // ...možnosti klienta
  });

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

Požadavky ​

Protože Vitest běží v Node.js, mockování síťových požadavků je složité; webová API nejsou k dispozici, takže potřebujeme něco, co bude napodobovat chování sítě. Doporučujeme Mock Service Worker k dosažení tohoto cíle. Umožní vám mockovat síťové požadavky REST i GraphQL a je nezávislý na frameworku.

Mock Service Worker (MSW) funguje tak, že zachycuje požadavky, které vaše testy provádějí, což vám umožňuje jej používat bez změny kódu vaší aplikace. V prohlížeči to používá Service Worker API. V Node.js a pro Vitest používá @mswjs/interceptors. Chcete-li se dozvědět více o MSW, přečtěte si jejich úvod.

Konfigurace ​

Můžete jej použít následujícím způsobem ve vašem konfiguračním souboru.

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

// Spuštění serveru před všemi testy
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// Ukončení serveru po všech testech
afterAll(() => server.close());

// Reset handlerů po každém testu (důležité pro izolaci testů)
afterEach(() => server.resetHandlers());

Konfigurace serveru s onUnhandleRequest: 'error' zajišťuje, že bude vyvolána chyba, kdykoli dojde k požadavku, který nemá odpovídající obslužnou rutinu.

Příklad ​

Máme plně funkční příklad, který používá MSW: React Testing with MSW.

Více ​

S MSW toho můžete dělat mnohem více. Můžete přistupovat k souborům cookie a parametrům dotazu, definovat mockované chybové odpovědi a mnoho dalšího! Pro více informací o MSW si přečtěte dokumentaci.

Časovače ​

Když testujeme kód, který zahrnuje časové intervaly nebo intervaly, místo toho, abychom nechali naše testy čekat nebo vypršet časový limit, můžeme naše testy urychlit pomocí "falešných" časovačů, které mockují volání setTimeout a setInterval.

Podrobnější popis API naleznete v sekci vi.useFakeTimers api.

Příklad ​

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);
    // posun o 2ms funkci nespustí
    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 v níže uvedených příkladech je importováno přímo z vitest. Můžete ho také používat globálně, pokud nastavíte globals na true ve vaší konfiguraci.

Chci…

Špehovat metodu instance ​

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

Mockovat exportované proměnné ​

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

Mockovat exportovanou funkci ​

  1. Příklad použití vi.mock:

WARNING

Nezapomeňte, že volání vi.mock je přesunuto na začátek souboru. Vždy bude provedeno před všemi importy.

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

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

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

Mockovat implementaci exportované třídy ​

  1. Příklad s vi.mock a .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 bude obsahovat instance třídy SomeClass
  1. Příklad s vi.mock a návratovou hodnotou:
ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn(() => ({
    someMethod: vi.fn(),
  }));
  return { SomeClass };
});
// SomeClass.mock.returns bude obsahovat vrácené objekty
  1. Příklad s vi.spyOn:
ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'SomeClass').mockImplementation(() => {
  // cokoliv vám vyhovuje z prvních dvou příkladů
});

Špehovat objekt vrácený z funkce ​

  1. Příklad použití 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(),
      };
    }
    // nyní pokaždé, když je volána funkce useObject(), vrátí stejnou referenci objektu
    return _cache;
  };
  return { useObject };
});

const obj = useObject();
// obj.method bylo voláno uvnitř some-path
expect(obj.method).toHaveBeenCalled();

Mockovat část modulu ​

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(); // má původní chování
mocked(); // je špehovací funkce

Mockovat aktuální datum ​

Pro mockování času Date můžete použít pomocnou funkci vi.setSystemTime. Tato hodnota se neresetuje automaticky mezi jednotlivými testy.

Mějte na paměti, že použití vi.useFakeTimers také změní čas Date.

ts
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// reset mockovaného času
vi.useRealTimers();

Mockovat globální proměnnou ​

Globální proměnnou můžete nastavit přiřazením hodnoty do globalThis nebo pomocí pomocníka vi.stubGlobal. Pokud použijete vi.stubGlobal, tato hodnota se neresetuje automaticky mezi jednotlivými testy, pokud nepovolíte volbu konfigurace unstubGlobals nebo nezavoláte vi.unstubAllGlobals.

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

Mockovat import.meta.env ​

  1. Chcete-li změnit proměnnou prostředí, můžete jí jednoduše přiřadit novou hodnotu.

WARNING

Hodnota proměnné prostředí se nebude automaticky resetovat mezi různými testy.

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

// můžete ji resetovat v beforeEach hooku manuálně
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. Chcete-li automaticky resetovat hodnoty, můžete použít pomocníka vi.stubEnv se zapnutou možností konfigurace unstubEnvs (nebo ručně zavolat vi.unstubAllEnvs](/api/vi#vi-unstuballenvs) v hooku beforeEach):
ts
import { expect, it, vi } from 'vitest';

// před spuštěním testů je "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 another test', () => {
  expect(import.meta.env.VITE_ENV).toBe('test');
});
ts
// vitest.config.ts
export default {
  test: {
    unstubAllEnvs: true,
  },
};
Pager
Předchozí stránkaSnímky
Další stránkaTestování typů

Vydáno pod licencí MIT.

Copyright (c) 2024 Mithril Contributors

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

Vydáno pod licencí MIT.

Copyright (c) 2024 Mithril Contributors