Skip to content
Vitest 0
Main Navigation PrzewodnikAPIKonfiguracjaZaawansowany
1.6.1
0.34.6

Polski

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

Polski

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

Wygląd

Sidebar Navigation

Przewodnik

Dlaczego Vitest

Wprowadzenie

Funkcje

Obszar roboczy

Interfejs Linii Poleceń

Filtrowanie Testów

Pokrycie kodu

Snapshot

Mockowanie

Testowanie Typów

Interfejs użytkownika Vitest

Tryb przeglądarki (eksperymentalny)

Testowanie w kodzie źródłowym

Kontekst Testowy

Środowisko Testowe

Rozszerzanie Matcherów

Integracje z IDE

Debugowanie

Porównania z innymi narzędziami do uruchamiania testów

Przewodnik migracji

Częste błędy

API

Dokumentacja API Testów

Funkcje Mockujące

Vi

expect

expectTypeOf

assertType

Konfiguracja

Konfiguracja Vitest

Na tej stronie

Mockowanie ​

Podczas pisania testów prędzej czy później pojawi się potrzeba stworzenia "fałszywej" wersji zależności wewnętrznej lub zewnętrznej. Jest to powszechnie nazywane mockowaniem. Vitest udostępnia funkcje pomocnicze, które ułatwiają ten proces za pomocą obiektu vi. Możesz użyć import { vi } from 'vitest' lub uzyskać do niego dostęp globalnie (gdy konfiguracja globalna jest włączona).

WARNING

Zawsze pamiętaj, aby wyczyścić lub przywrócić mocki przed lub po każdym teście, aby uniknąć niepożądanych zmian stanu między testami! Więcej informacji znajdziesz w dokumentacji mockReset.

Jeśli chcesz od razu przejść do konkretnych rozwiązań, sprawdź sekcję API; w przeciwnym razie kontynuuj czytanie, aby dowiedzieć się więcej o mockowaniu.

Daty ​

Czasami konieczne jest kontrolowanie daty, aby zapewnić spójność testów. Vitest używa pakietu @sinonjs/fake-timers do manipulowania czasomierzami, a także datą systemową. Szczegółowe informacje na temat konkretnego API można znaleźć tutaj.

Przykład ​

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(() => {
    // tell vitest we use mocked time
    vi.useFakeTimers();
  });

  afterEach(() => {
    // restoring date after each test run
    vi.useRealTimers();
  });

  it('allows purchases within business hours', () => {
    // set hour within business hours
    const date = new Date(2000, 1, 1, 13);
    vi.setSystemTime(date);

    // access Date.now() will result in the date set above
    expect(purchase()).toEqual({ message: 'Success' });
  });

  it('disallows purchases outside of business hours', () => {
    // set hour outside business hours
    const date = new Date(2000, 1, 1, 19);
    vi.setSystemTime(date);

    // access Date.now() will result in the date set above
    expect(purchase()).toEqual({ message: 'Error' });
  });
});

Funkcje ​

Mockowanie funkcji dzieli się na dwie kategorie: szpiegowanie (ang. spying) i mockowanie (ang. mocking).

Czasami wystarczy sprawdzić, czy dana funkcja została wywołana (i ewentualnie z jakimi argumentami). W takich przypadkach szpiegowanie jest wystarczające i można je zrealizować bezpośrednio za pomocą vi.spyOn() (więcej informacji tutaj).

Jednak szpiedzy (ang. spies) służą tylko do obserwowania funkcji, nie mogą zmieniać ich implementacji. Jeśli potrzebujemy utworzyć fałszywą (lub mockowaną) wersję funkcji, możemy użyć vi.fn() (więcej informacji tutaj).

Używamy Tinyspy jako podstawy do mockowania funkcji, ale mamy własną otoczkę, aby zapewnić kompatybilność z jest. Zarówno vi.fn() i vi.spyOn() współdzielą te same metody, jednak tylko wynik zwracany przez vi.fn() jest wywoływalny.

Przykład ​

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

Więcej ​

  • Funkcje Mock w Jest

Zmienne globalne ​

Możesz mockować zmienne globalne, które nie są dostępne w jsdom lub node, używając pomocnika vi.stubGlobal. Umieści on wartość zmiennej globalnej w obiekcie 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);

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

Moduły ​

Mockowanie modułów pozwala obserwować biblioteki firm trzecich, które są wywoływane w kodzie, umożliwiając testowanie argumentów, wyników, a nawet ponowne definiowanie ich implementacji.

Zobacz sekcję API vi.mock(), aby uzyskać bardziej szczegółowy opis API.

Algorytm automatycznego mockowania ​

Jeśli twój kod importuje mockowany moduł, bez powiązanego pliku __mocks__ lub factory dla tego modułu, Vitest automatycznie zamockuje moduł, wywołując go i mockując każdy eksport.

Obowiązują następujące zasady:

  • Wszystkie tablice zostaną opróżnione
  • Wszystkie typy proste i kolekcje pozostaną bez zmian
  • Wszystkie obiekty zostaną głęboko sklonowane
  • Wszystkie instancje klas i ich prototypy zostaną głęboko sklonowane

Przykład ​

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} element(ów) zwróconych`,
      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 });
  });
});

Żądania ​

Ponieważ Vitest działa w Node.js, mockowanie żądań sieciowych jest utrudnione; interfejsy API przeglądarki nie są dostępne, więc potrzebujemy czegoś, co będzie naśladować zachowanie sieci. Zalecamy Mock Service Worker do tego celu. Pozwoli to na mockowanie zarówno żądań sieciowych REST, jak i GraphQL i jest niezależne od frameworka.

Mock Service Worker (MSW) działa poprzez przechwytywanie żądań wykonywanych przez testy, co pozwala na używanie go bez zmiany kodu aplikacji. W przeglądarce używa Service Worker API. W Node.js i dla Vitest używa node-request-interceptor. Aby dowiedzieć się więcej o MSW, przeczytaj wprowadzenie

Konfiguracja ​

Możesz go użyć w swoim pliku konfiguracyjnym w następujący sposób:

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

// Start server before all tests
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

//  Close server after all tests
afterAll(() => server.close());

// Reset handlers after each test `important for test isolation`
afterEach(() => server.resetHandlers());

Skonfigurowanie serwera z onUnhandleRequest: 'error' zapewnia, że zostanie zgłoszony błąd, gdy tylko pojawi się żądanie, dla którego nie zdefiniowano odpowiedniego handlera.

Przykład ​

Mamy w pełni działający przykład, który używa MSW: React Testing with MSW.

Więcej ​

MSW ma o wiele więcej do zaoferowania. Możesz uzyskać dostęp do plików cookie i parametrów zapytania, definiować mockowane odpowiedzi błędów i wiele więcej! Aby zobaczyć wszystko, co możesz zrobić z MSW, przeczytaj ich dokumentację.

Timery ​

Podczas testowania kodu zawierającego limity czasu lub interwały, zamiast czekać na zakończenie testów lub przekroczenie limitu czasu, możemy przyspieszyć nasze testy, używając mockowanych timerów, które mockują wywołania setTimeout i setInterval.

Zobacz sekcję API vi.useFakeTimers, aby uzyskać bardziej szczegółowy opis API.

Przykład ​

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

Podręczny zestaw ​

INFO

vi w poniższych przykładach jest importowane bezpośrednio z vitest. Możesz również używać go globalnie, jeśli ustawisz opcję globals na true w swojej konfiguracji.

Chcę…

  • Szpiegować metodę
ts
const instance = new SomeClass();
vi.spyOn(instance, 'method');
  • Mockować eksportowane zmienne
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');
  • Mockować eksportowaną funkcję

Przykład z użyciem 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

Pamiętaj, że wywołania vi.mock są podnoszone (hoistowane) na początek pliku. Nie umieszczaj wywołań vi.mock wewnątrz beforeEach, ponieważ tylko jedno z nich faktycznie zamockuje moduł.

Przykład z użyciem vi.spyOn:

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

vi.spyOn(exports, 'method').mockImplementation(() => {});
  • Mockować implementację eksportowanej klasy

Przykład z vi.mock i prototypem:

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 będzie zawierać instancje SomeClass

Przykład z vi.mock i wartością zwracaną:

ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn(() => ({
    someMethod: vi.fn(),
  }));
  return { SomeClass };
});
// SomeClass.mock.results będzie zawierać zwrócone obiekty

Przykład z vi.spyOn:

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

vi.spyOn(exports, 'SomeClass').mockImplementation(() => {
  // cokolwiek pasuje z dwóch pierwszych przykładów
});
  • Szpiegować obiekt zwracany z funkcji

Przykład użycia 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(),
      };
    }
    // teraz każde wywołanie useObject() zwróci tę samą referencję do obiektu
    return _cache;
  };
  return { useObject };
});

const obj = useObject();
// obj.method została wywołana wewnątrz some-path
expect(obj.method).toHaveBeenCalled();
  • Mockować część modułu
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(); // ma oryginalne zachowanie
mocked(); // jest funkcją szpiegującą
  • Mockować aktualną datę

Aby zamockować czas Date, możesz użyć funkcji pomocniczej vi.setSystemTime. Ta wartość nie jest automatycznie resetowana między testami.

Pamiętaj, że użycie vi.useFakeTimers również wpływa na czas Date.

ts
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// resetowanie zamockowanego czasu
vi.useRealTimers();
  • Mockować zmienną globalną

Możesz ustawić zmienną globalną, przypisując wartość do globalThis lub używając funkcji pomocniczej vi.stubGlobal. Używając vi.stubGlobal, wartość nie jest automatycznie resetowana między testami, chyba że włączysz opcję konfiguracji unstubGlobals lub wywołasz vi.unstubAllGlobals.

ts
vi.stubGlobal('__VERSION__', '1.0.0');
expect(__VERSION__).toBe('1.0.0');
  • Mockować import.meta.env

Aby zmienić zmienną środowiskową, możesz po prostu przypisać jej nową wartość. Ta wartość nie jest automatycznie resetowana między testami.

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

// możesz zresetować ją ręcznie w hooku beforeEach
const originalViteEnv = import.meta.env.VITE_ENV;

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

it('zmienia wartość', () => {
  import.meta.env.VITE_ENV = 'staging';
  expect(import.meta.env.VITE_ENV).toBe('staging');
});

Jeśli chcesz automatycznie zresetować wartość, możesz użyć funkcji pomocniczej vi.stubEnv z włączoną opcją konfiguracji unstubEnvs (lub wywołać vi.unstubAllEnvs ręcznie w hooku beforeEach):

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

// przed uruchomieniem testów "VITE_ENV" ma wartość "test"
import.meta.env.VITE_ENV === 'test';

it('zmienia wartość', () => {
  vi.stubEnv('VITE_ENV', 'staging');
  expect(import.meta.env.VITE_ENV).toBe('staging');
});

it('wartość jest przywracana przed uruchomieniem kolejnego testu', () => {
  expect(import.meta.env.VITE_ENV).toBe('test');
});
ts
// vitest.config.ts
export default {
  test: {
    unstubAllEnvs: true,
  },
};
Pager
Poprzednia stronaSnapshot
Następna stronaTestowanie Typów

Opublikowano na licencji MIT.

Copyright (c) 2024 Mithril Contributors

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

Opublikowano na licencji MIT.

Copyright (c) 2024 Mithril Contributors