Pytania rekrutacyjne Jest
55 pytań — 20 odpowiedzi dostępnych od razu
Podstawy Jest
Czym jest Jest i dlaczego jest tak popularny w ekosystemie JavaScript?
Odpowiedź w 30 sekund: Jest to framework testowy stworzony przez Facebooka (obecnie utrzymywany przez OpenJS Foundation), zaprojektowany jako kompletne rozwiązanie "wszystko w jednym" do testowania kodu JavaScript. Popularność zawdzięcza zerowej konfiguracji, wbudowanym asercjom, mockom i pokryciu kodu oraz świetnej integracji z Reactem i innymi nowoczesnymi narzędziami.
Odpowiedź w 2 minuty: Jest powstał w 2014 roku w Facebooku jako odpowiedź na potrzebę testowania ogromnej bazy kodu React. Jego głównym założeniem było zapewnienie narzędzia, które "po prostu działa" bez konieczności składania ekosystemu z wielu bibliotek (jak w przypadku Mocha + Chai + Sinon + Istanbul). Dziś jest standardem de facto w projektach Reactowych, Next.js, a także często wybierany w aplikacjach Node.js oraz bibliotekach uniwersalnych.
Popularność Jest wynika z kilku czynników: prostej konfiguracji (w wielu projektach wystarczy npm install --save-dev jest), wbudowanego runnera, asercji (expect), mocków (jest.fn, jest.mock), pokrycia kodu (--coverage), a także unikalnych funkcji jak snapshot testing. Dodatkowo Jest działa w izolowanych procesach Node.js, co daje deterministyczne i równoległe wykonywanie testów.
Warto używać Jest, gdy zależy nam na szybkim starcie, dobrej dokumentacji i wsparciu społeczności. Typowe pułapki to: domyślne środowisko jsdom (od Jest 27 zmienione na node — łatwo zapomnieć ustawić testEnvironment: 'jsdom' dla testów DOM-owych), problemy z transformacją ESM/TypeScript wymagające babel-jest lub ts-jest, oraz mockowanie modułów ESM (które do niedawna było eksperymentalne).
Przykład kodu:
// suma.js - prosty moduł do testowania
function suma(a, b) {
return a + b;
}
module.exports = { suma };
// suma.test.js - test napisany w Jest
const { suma } = require('./suma');
test('dodaje 2 + 3 i zwraca 5', () => {
// Asercja wbudowana - nie potrzeba Chai ani innej biblioteki
expect(suma(2, 3)).toBe(5);
});
test('obsługuje liczby ujemne', () => {
expect(suma(-1, -1)).toBe(-2);
});
Materiały
Jakie są kluczowe cechy Jest, które wyróżniają go na tle innych frameworków testowych?
Odpowiedź w 30 sekund: Jest wyróżnia się modelem "batteries-included" — w jednym pakiecie dostajemy runner, asercje, mocki, snapshot testing oraz raporty pokrycia kodu. Dodatkowo oferuje równoległe wykonywanie testów w izolowanych procesach, tryb watch oraz świetne komunikaty błędów.
Odpowiedź w 2 minuty:
Najważniejsze cechy Jest to: zerowa konfiguracja dla większości projektów (zwłaszcza Create React App, Next.js), wbudowane asercje przez API expect (toBe, toEqual, toHaveBeenCalled itd.), system mockowania (jest.fn, jest.spyOn, jest.mock) pozwalający na podmianę całych modułów, oraz snapshot testing — porównywanie wyniku komponentu/struktury z zapisanym wcześniej "zdjęciem".
Kolejne istotne cechy to izolacja testów — każdy plik testowy działa we własnym kontekście (workerze), co eliminuje problemy ze współdzielonym stanem. Równoległość sprawia, że duże suity testów działają znacznie szybciej. Tryb watch (jest --watch) inteligentnie uruchamia tylko testy powiązane ze zmienionymi plikami (poprzez analizę grafu zależności git). Code coverage (--coverage) działa out-of-the-box z wykorzystaniem Istanbul/V8.
Warto wspomnieć o timer mocks (jest.useFakeTimers), które pozwalają testować kod oparty o setTimeout/setInterval bez czekania, oraz o module mocking — możliwości podmiany całego modułu zewnętrznego (np. axios) jedną linią kodu. Pułapką bywa to, że tak rozbudowany framework może maskować problemy projektowe — gdy mockujemy "wszystko", nasze testy przestają cokolwiek weryfikować w realnym świecie.
Przykład kodu:
// Przykład pokazujący kilka kluczowych cech Jest naraz
const axios = require('axios');
jest.mock('axios'); // Automatyczne mockowanie modułu
describe('Pobieranie użytkownika', () => {
beforeEach(() => {
// Czyszczenie wywołań mocków przed każdym testem
jest.clearAllMocks();
});
test('pobiera dane i tworzy snapshot', async () => {
// Mockowanie odpowiedzi HTTP
axios.get.mockResolvedValue({ data: { id: 1, name: 'Anna' } });
const response = await axios.get('/api/user/1');
// Asercja - sprawdzenie wywołania mocka
expect(axios.get).toHaveBeenCalledWith('/api/user/1');
// Snapshot - porównanie ze "zdjęciem" zapisanym przy pierwszym uruchomieniu
expect(response.data).toMatchSnapshot();
});
});
Materiały
Jaka jest różnica między Jest a Mocha lub Jasmine?
Odpowiedź w 30 sekund: Mocha to minimalistyczny test runner wymagający dodatkowych bibliotek do asercji (Chai) i mocków (Sinon). Jasmine to framework "wszystko w jednym" — zbliżony filozofią do Jest, ale starszy i pozbawiony nowoczesnych funkcji jak snapshot testing czy równoległe wykonywanie. Jest łączy zalety obu: kompletność Jasmine i nowoczesność Mocha.
Odpowiedź w 2 minuty: Mocha (od 2011) to bardzo elastyczny runner, który celowo nie narzuca asercji ani biblioteki mockującej — pozwala dobrać dowolne narzędzia (Chai, Sinon, Should.js, NYC dla coverage). To podejście daje dużą swobodę, ale wymaga większej konfiguracji i znajomości ekosystemu. Mocha jest popularna w bibliotekach Node.js oraz w starszych projektach.
Jasmine (od 2010, stworzony przez Pivotal Labs) to framework BDD oferujący kompletne rozwiązanie: runner, asercje (expect), mocki (spyOn). Składnia (describe, it, beforeEach) była inspiracją dla Jest. Jest najczęściej używany w starszych projektach Angular (przed migracją na Karma+Jasmine, a obecnie często do Jest).
Jest (od 2014) zbudowano bazując na Jasmine, ale dodano: równoległość, snapshoty, lepsze mocki, tryb watch, izolację modułów oraz wbudowane coverage. W praktyce Jest jest dziś najpopularniejszy w świecie React/Next/TypeScript, podczas gdy Mocha utrzymuje silną pozycję w projektach Node.js i bibliotekach niewykorzystujących Reacta.
Wybór zależy od projektu: Jest dla aplikacji frontendowych i monorepo, Mocha dla bibliotek Node.js wymagających minimalnych zależności, Jasmine — głównie w istniejących projektach.
| Cecha | Jest | Mocha | Jasmine |
|---|---|---|---|
| Runner | tak | tak | tak |
| Wbudowane asercje | tak (expect) |
nie (Chai) | tak (expect) |
| Wbudowane mocki | tak (jest.fn) |
nie (Sinon) | tak (spyOn) |
| Snapshot testing | tak | nie | nie |
| Coverage | tak (wbudowany) | nie (NYC) | nie |
| Równoległość | tak | tylko z mocha-parallel | nie |
| Tryb watch | tak (smart) | tak (--watch) |
nie |
| Konfiguracja | minimalna | wymagana | minimalna |
| Popularność (2024) | bardzo wysoka | wysoka | niska |
Przykład kodu:
// Ten sam test w trzech frameworkach
// === MOCHA + CHAI ===
const { expect } = require('chai');
describe('Suma', () => {
it('dodaje liczby', () => {
expect(2 + 3).to.equal(5); // Chai - inny styl asercji
});
});
// === JASMINE ===
describe('Suma', () => {
it('dodaje liczby', () => {
expect(2 + 3).toBe(5); // Wbudowane expect
});
});
// === JEST ===
describe('Suma', () => {
test('dodaje liczby', () => {
expect(2 + 3).toBe(5); // Praktycznie identyczne jak Jasmine
});
});
Materiały
Jak Jest odnajduje pliki testowe w projekcie (testMatch, testPathIgnorePatterns)?
Odpowiedź w 30 sekund:
Jest domyślnie wyszukuje pliki w katalogach __tests__ oraz pliki kończące się na .test.js, .spec.js (i ich warianty z TypeScript/JSX). Zachowanie można dostosować poprzez opcje testMatch (lista glob patterns) oraz testPathIgnorePatterns (ścieżki do pominięcia, domyślnie node_modules).
Odpowiedź w 2 minuty:
Domyślna konfiguracja testMatch w Jest wygląda tak: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"]. Oznacza to, że Jest znajdzie pliki w katalogach __tests__ (na dowolnym poziomie zagnieżdżenia) oraz wszystkie pliki o rozszerzeniu .test.js, .test.ts, .test.jsx, .test.tsx, .spec.js itd.
testPathIgnorePatterns domyślnie ma wartość ["/node_modules/"], więc Jest nie skanuje zależności. Często dodaje się tu również /dist/, /build/, /.next/ lub inne katalogi build-output, by uniknąć podwójnego uruchamiania testów na skompilowanym kodzie.
Alternatywą dla testMatch jest testRegex (wzajemnie się wykluczają) — wyrażenie regularne dopasowujące ścieżki testów. Generalnie zaleca się testMatch, bo glob jest czytelniejszy.
Warto pamiętać o rootDir (domyślnie katalog z package.json) i roots (lista katalogów, w których Jest szuka testów — domyślnie ["<rootDir>"]). W monorepo często ustawia się roots: ["<rootDir>/packages"] lub używa projects dla wielu konfiguracji.
Pułapki: gdy używamy katalogu __tests__, każdy plik tam trafiający — także helpers.js czy setup.js — zostanie potraktowany jako test (i Jest rzuci błąd "Your test suite must contain at least one test"). Rozwiązaniem jest umieszczanie helperów poza __tests__ albo dodanie ich do testPathIgnorePatterns.
Przykład kodu:
// jest.config.js
module.exports = {
// Gdzie Jest ma szukać testów
roots: ['<rootDir>/src', '<rootDir>/tests'],
// Wzorce dopasowania plików testowych
testMatch: [
'**/__tests__/**/*.[jt]s?(x)', // Katalogi __tests__
'**/?(*.)+(spec|test).[jt]s?(x)' // *.test.js, *.spec.ts itp.
],
// Ścieżki ignorowane (regex, nie glob!)
testPathIgnorePatterns: [
'/node_modules/',
'/dist/',
'/build/',
'/__tests__/helpers/' // Pomijamy helpery w __tests__
],
// Alternatywa - testRegex (zamiast testMatch)
// testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$',
};
// Aby zobaczyć, które pliki Jest znajdzie, uruchom:
// npx jest --listTests
Materiały
Jaka jest różnica między test(), it() oraz describe()?
Odpowiedź w 30 sekund:
test() i it() to ten sam mechanizm — aliasy definiujące pojedynczy przypadek testowy; różnią się tylko stylem językowym. describe() grupuje powiązane testy w blok logiczny, co poprawia czytelność wyjścia i pozwala dzielić beforeEach/afterEach.
Odpowiedź w 2 minuty:
test(name, fn) to podstawowa funkcja Jest definiująca przypadek testowy. it(name, fn) jest jej dokładnym aliasem — istnieje, by umożliwić styl BDD przejęty z Jasmine, gdzie zdanie czytane jest naturalnie: it("should return 5", ...). Wybór między nimi to kwestia konwencji projektu — żadnej różnicy technicznej nie ma.
describe(name, fn) to grupowanie — tworzy blok, w którym możemy zorganizować logicznie powiązane testy. Wyjście Jest pokazuje wtedy hierarchię (zagnieżdżone bullet points), co ułatwia nawigację w dużych plikach. Wewnątrz describe możemy zdefiniować hooki: beforeAll, beforeEach, afterEach, afterAll, które obowiązują tylko w obrębie tego bloku (i jego zagnieżdżonych describe).
Konwencja: nazwa describe opisuje co testujemy (klasa, moduł, funkcja, scenariusz), nazwa test/it opisuje jaki konkretny przypadek. Często stosuje się zagnieżdżone describe dla różnych scenariuszy tej samej funkcji (np. "z poprawnymi danymi", "z błędnymi danymi").
Modyfikatory: test.only / it.only / describe.only uruchamiają tylko zaznaczone testy (przydatne przy debugowaniu); test.skip / describe.skip pomijają je; test.todo("opis") zaznacza testy do napisania.
Przykład kodu:
// Kalkulator - przykład grupowania describe + test/it
const Kalkulator = require('./kalkulator');
describe('Kalkulator', () => {
let kalkulator;
// Hook - uruchamiany przed KAŻDYM testem w tym describe
beforeEach(() => {
kalkulator = new Kalkulator();
});
describe('metoda dodaj()', () => {
// 'test' i 'it' są identyczne
test('dodaje dwie liczby dodatnie', () => {
expect(kalkulator.dodaj(2, 3)).toBe(5);
});
it('powinno obsłużyć liczby ujemne', () => {
// Styl BDD - "it should..."
expect(kalkulator.dodaj(-1, -1)).toBe(-2);
});
});
describe('metoda podziel()', () => {
test('dzieli dwie liczby', () => {
expect(kalkulator.podziel(10, 2)).toBe(5);
});
test('rzuca wyjątek przy dzieleniu przez zero', () => {
expect(() => kalkulator.podziel(10, 0)).toThrow('Dzielenie przez zero');
});
test.todo('zaokrąglanie wyniku do 2 miejsc po przecinku');
});
});
// Wyjście Jest pokazuje hierarchię:
// Kalkulator
// metoda dodaj()
// ✓ dodaje dwie liczby dodatnie
// ✓ powinno obsłużyć liczby ujemne
// metoda podziel()
// ✓ dzieli dwie liczby
// ✓ rzuca wyjątek przy dzieleniu przez zero
// ✎ todo zaokrąglanie wyniku do 2 miejsc po przecinku
Materiały
Czym jest piramida testów i jak Jest wpisuje się w jej poziomy?
Odpowiedź w 30 sekund: Piramida testów (Mike Cohn, 2009) to model wizualizujący proporcje typów testów: dużo szybkich testów jednostkowych u podstawy, mniej testów integracyjnych pośrodku, niewiele wolnych testów E2E na szczycie. Jest doskonale sprawdza się na poziomie testów jednostkowych i integracyjnych — dla E2E używa się raczej Playwright/Cypress.
Odpowiedź w 2 minuty: Koncepcja piramidy testów zakłada, że dobrze zbalansowana suita testów ma kształt piramidy:
- Podstawa (~70%): testy jednostkowe — szybkie, izolowane, sprawdzające pojedyncze funkcje/klasy/komponenty;
- Środek (~20%): testy integracyjne — weryfikujące współpracę kilku modułów (np. komponent + API + store);
- Wierzchołek (~10%): testy E2E (end-to-end) — uruchamiają całą aplikację w przeglądarce, symulują użytkownika.
Im wyżej, tym testy są wolniejsze, droższe w utrzymaniu i bardziej "kruche" (flaky). Im niżej — szybsze, łatwiejsze do debugowania, ale obejmują mniejszy zakres.
Jest świetnie nadaje się na dwa dolne poziomy: testy jednostkowe (np. czysta funkcja, reducer, hook) oraz testy integracyjne (komponent React z React Testing Library, gdzie sprawdzamy interakcję z DOM, API mockowanym przez MSW itp.). Na poziomie E2E Jest jest rzadziej używany — preferuje się Playwright, Cypress lub WebdriverIO, które uruchamiają realną przeglądarkę.
Alternatywne modele to plaster miodu (Spotify) — więcej testów integracyjnych, mniej jednostkowych, gdy logika biznesowa jest w warstwie integracji; oraz trofeum testowe Kenta C. Doddsa, gdzie integracyjne stanowią największą warstwę. Niezależnie od modelu, Jest pokrywa dolne i środkowe warstwy w stacku JavaScript/TypeScript.
flowchart TD
E2E["Testy E2E<br/>~10%<br/>Playwright, Cypress<br/>Wolne, drogie"]
INT["Testy integracyjne<br/>~20%<br/>Jest + RTL + MSW<br/>Współpraca modułów"]
UNIT["Testy jednostkowe<br/>~70%<br/>Jest<br/>Szybkie, izolowane"]
E2E --> INT
INT --> UNIT
style E2E fill:#ffcccc
style INT fill:#ffeecc
style UNIT fill:#ccffcc
Przykład kodu:
// === POZIOM 1: TEST JEDNOSTKOWY (Jest) ===
// formatPrice.js
function formatPrice(amount, currency = 'PLN') {
return `${amount.toFixed(2)} ${currency}`;
}
// formatPrice.test.js - czysta funkcja, brak zależności
test('formatuje cenę z domyślną walutą', () => {
expect(formatPrice(19.9)).toBe('19.90 PLN');
});
// === POZIOM 2: TEST INTEGRACYJNY (Jest + React Testing Library) ===
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import CartSummary from './CartSummary';
test('użytkownik dodaje produkt i widzi sumę w PLN', async () => {
render(<CartSummary />);
// Symulacja interakcji - test obejmuje komponent, hook i formatPrice
await userEvent.click(screen.getByRole('button', { name: /dodaj/i }));
expect(screen.getByText('19.90 PLN')).toBeInTheDocument();
});
// === POZIOM 3: TEST E2E (zazwyczaj Playwright, NIE Jest) ===
// e2e/checkout.spec.ts
// import { test, expect } from '@playwright/test';
// test('pełna ścieżka zakupu', async ({ page }) => {
// await page.goto('/sklep');
// await page.click('text=Dodaj do koszyka');
// await expect(page.locator('.cart-total')).toHaveText('19.90 PLN');
// });
Materiały
Konfiguracja i Setup
Jak zainstalować i skonfigurować Jest w projekcie React/TypeScript?
Odpowiedź w 30 sekund:
Instalujemy jest, @types/jest, ts-jest oraz @testing-library/react jako devDependencies, a następnie tworzymy plik jest.config.js z presetem ts-jest i środowiskiem jsdom. W projektach opartych na Create React App lub Vite konfiguracja jest często gotowa lub wymaga jedynie drobnych modyfikacji.
Odpowiedź w 2 minuty:
W standardowym projekcie React z TypeScript musimy zainstalować kilka kluczowych pakietów: jest jako test runner, ts-jest do transformacji TypeScript, @types/jest dla typowania, jest-environment-jsdom dla symulacji środowiska przeglądarki oraz @testing-library/react i @testing-library/jest-dom do testowania komponentów. Następnie generujemy konfigurację przez npx ts-jest config:init lub tworzymy ręcznie plik jest.config.js.
W konfiguracji ustawiamy preset: 'ts-jest', testEnvironment: 'jsdom', definiujemy ścieżki testów przez testMatch lub testRegex, a dla obsługi importów CSS i assetów konfigurujemy moduleNameMapper. Warto też dodać setupFilesAfterEach z @testing-library/jest-dom żeby uzyskać dodatkowe matchery jak toBeInTheDocument.
Jeśli używamy Create React App, Jest jest już skonfigurowany — wystarczy uruchomić npm test. W projektach Vite zaleca się Vitest, ale można też skonfigurować Jest ręcznie. W Next.js 12+ konfiguracja Jest jest opisana w oficjalnej dokumentacji i wymaga użycia next/jest dla automatycznej obsługi SWC.
Przykład kodu:
// Instalacja pakietów
// npm install --save-dev jest @types/jest ts-jest jest-environment-jsdom
// npm install --save-dev @testing-library/react @testing-library/jest-dom
// jest.config.js - konfiguracja dla React + TypeScript
module.exports = {
// Preset ts-jest obsługuje transformację TypeScript
preset: 'ts-jest',
// Środowisko jsdom symuluje DOM przeglądarki
testEnvironment: 'jsdom',
// Pliki uruchamiane po inicjalizacji frameworka testowego
setupFilesAfterEach: ['<rootDir>/jest.setup.ts'],
// Mapowanie modułów - obsługa importów CSS i aliasów ścieżek
moduleNameMapper: {
'\\.(css|less|scss)$': 'identity-obj-proxy',
'^@/(.*)$': '<rootDir>/src/$1',
},
// Wzorzec wyszukiwania plików testowych
testMatch: ['**/__tests__/**/*.test.(ts|tsx)'],
};
// jest.setup.ts - dodatkowe matchery z jest-dom
import '@testing-library/jest-dom';
// package.json - skrypty
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
Materiały
Jakie są kluczowe opcje pliku jest.config.js (testEnvironment, transform, moduleNameMapper)?
Odpowiedź w 30 sekund:
Najważniejsze opcje to testEnvironment (środowisko wykonania testów — jsdom lub node), transform (mapowanie wzorców plików na transformery jak ts-jest czy babel-jest) oraz moduleNameMapper (regex-mapping importów do mocków lub aliasów). Razem definiują jak Jest interpretuje, transformuje i wykonuje kod testowy.
Odpowiedź w 2 minuty:
testEnvironment określa kontekst, w którym uruchamiane są testy. Wartość 'jsdom' symuluje przeglądarkę z dostępem do window, document i DOM, podczas gdy 'node' jest lżejszym środowiskiem dla testów backendowych. Można też ustawić środowisko per-plik przez komentarz @jest-environment na początku pliku testowego.
transform to obiekt mapujący wzorce regex na transformery, które konwertują kod przed wykonaniem. Najczęściej '^.+\\.tsx?$': 'ts-jest' dla TypeScript lub '^.+\\.jsx?$': 'babel-jest' dla JSX. Transformery cache'ują wyniki, więc kolejne uruchomienia są szybsze.
moduleNameMapper rozwiązuje importy przez regex — używany do mockowania CSS ('\\.css$': 'identity-obj-proxy'), assetów statycznych, oraz aliasów ścieżek ('^@/(.*)$': '<rootDir>/src/$1'). Inne ważne opcje to testMatch/testRegex (lokalizacja testów), collectCoverageFrom (zakres pomiaru pokrycia), setupFiles i setupFilesAfterEach (skrypty inicjalizacyjne), globalSetup/globalTeardown (raz przed/po całej suite), moduleDirectories (gdzie szukać modułów) oraz clearMocks: true (auto-czyszczenie mocków między testami).
Przykład kodu:
// jest.config.js - przegląd najważniejszych opcji
module.exports = {
// Środowisko testowe: 'jsdom' dla frontendu, 'node' dla backendu
testEnvironment: 'jsdom',
// Transformacja plików przed wykonaniem
transform: {
'^.+\\.tsx?$': ['ts-jest', { tsconfig: 'tsconfig.test.json' }],
'^.+\\.jsx?$': 'babel-jest',
},
// Mapowanie modułów - mocki i aliasy
moduleNameMapper: {
// Mockowanie plików CSS/SCSS
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
// Mockowanie obrazów i innych assetów
'\\.(jpg|jpeg|png|svg)$': '<rootDir>/__mocks__/fileMock.js',
// Aliasy ścieżek
'^@components/(.*)$': '<rootDir>/src/components/$1',
},
// Wyszukiwanie testów
testMatch: ['**/*.test.(ts|tsx|js|jsx)'],
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
// Pliki konfiguracyjne uruchamiane raz na worker
setupFiles: ['<rootDir>/jest.polyfills.js'],
// Pliki uruchamiane po inicjalizacji każdego testu
setupFilesAfterEach: ['<rootDir>/jest.setup.ts'],
// Pokrycie kodu
collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/**/*.d.ts'],
coverageThreshold: {
global: { branches: 80, functions: 80, lines: 80 },
},
// Auto-czyszczenie mocków między testami
clearMocks: true,
restoreMocks: true,
};
Materiały
Czym jest różnica między środowiskami jsdom a node i kiedy wybrać które?
Odpowiedź w 30 sekund:
jsdom symuluje przeglądarkę z DOM, window, document i większością Web API — idealne dla testów komponentów React i kodu używającego DOM. node to lekkie środowisko bez DOM, szybsze i mniej zasobożerne — odpowiednie dla testów logiki biznesowej, funkcji utility i kodu serwerowego.
Odpowiedź w 2 minuty:
jsdom to czysto-JavaScriptowa implementacja standardów DOM i HTML, która tworzy wirtualne środowisko przeglądarki w Node.js. Daje dostęp do window, document, localStorage, fetch (od jsdom 16+ z polyfillem), eventów DOM oraz wielu Web API. Jest niezbędne do testowania komponentów React, manipulacji DOM, hooks używających DOM oraz interakcji użytkownika. Ma jednak narzut — testy są wolniejsze, a inicjalizacja środowiska zajmuje czas.
node to natywne środowisko Node.js bez żadnej symulacji przeglądarki. Brak window, document, ale dostępne są wszystkie Node.js API jak fs, path, process. Jest znacznie szybsze, idealne dla testów funkcji pure, klas, parserów, walidatorów, kodu Express/NestJS oraz logiki biznesowej niezależnej od UI.
Wybór zależy od testowanego kodu. W projekcie hybrydowym (np. Next.js) można mieszać środowiska per-plik przez komentarz /** @jest-environment node */ lub /** @jest-environment jsdom */ na początku pliku testowego. Niektóre projekty konfigurują nawet osobne projekty Jest dla różnych typów testów przez opcję projects w jest.config.js.
| Cecha | jsdom |
node |
|---|---|---|
| DOM API | Tak (window, document) | Nie |
| localStorage / sessionStorage | Tak | Nie |
| fetch API | Tak (jsdom 16+) | Tak (Node 18+) |
| Wydajność | Wolniejsze (narzut DOM) | Szybsze |
| Rozmiar pamięci | Większy | Mniejszy |
| Testy komponentów React | Tak | Nie |
| Testy Express/API | Słabo | Idealne |
| Testy logiki/utili | Działa, ale narzut | Idealne |
| Domyślne w CRA | Tak | Nie |
Przykład kodu:
// jest.config.js - środowisko globalne
module.exports = {
testEnvironment: 'jsdom', // domyślnie dla projektów frontendowych
};
// Nadpisanie środowiska per plik testowy
/**
* @jest-environment node
*/
// validator.test.ts - test logiki bez DOM
import { validateEmail } from './validator';
describe('validateEmail', () => {
it('powinien zwrócić true dla poprawnego emaila', () => {
expect(validateEmail('test@example.com')).toBe(true);
});
});
// Inny plik - test komponentu React (jsdom)
/**
* @jest-environment jsdom
*/
import { render, screen } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
it('powinien wyrenderować tekst', () => {
// Wymaga DOM - jsdom dostarcza document
render(<Button>Kliknij mnie</Button>);
expect(screen.getByText('Kliknij mnie')).toBeInTheDocument();
});
});
// Konfiguracja wielu projektów - osobne środowiska
// jest.config.js
module.exports = {
projects: [
{
displayName: 'frontend',
testMatch: ['<rootDir>/src/components/**/*.test.tsx'],
testEnvironment: 'jsdom',
},
{
displayName: 'backend',
testMatch: ['<rootDir>/src/api/**/*.test.ts'],
testEnvironment: 'node',
},
],
};
Materiały
Jak skonfigurować Jest do pracy z TypeScript za pomocą ts-jest lub Babela?
Odpowiedź w 30 sekund:
ts-jest używa kompilatora TypeScript do transformacji i sprawdzania typów podczas testów — wolniejsze, ale dokładne. Babel (z @babel/preset-typescript) tylko usuwa typy bez sprawdzania — znacznie szybsze, ale bez bezpieczeństwa typów. Wybór zależy od priorytetu: bezpieczeństwo typów vs wydajność.
Odpowiedź w 2 minuty:
ts-jest to transformer dla Jest, który używa oficjalnego kompilatora TypeScript (tsc) do transformacji plików .ts/.tsx na JavaScript. Zachowuje pełne sprawdzanie typów podczas testów, co oznacza, że błąd typu spowoduje niepowodzenie testu. Konfiguracja jest prosta: npm install --save-dev ts-jest, potem npx ts-jest config:init generuje konfigurację. Można go dostrajać przez opcję globals lub przekazywanie konfiguracji w transform. W nowszych wersjach (29+) zalecane jest użycie funkcji createDefaultPreset() zamiast preset: 'ts-jest'.
Babel z @babel/preset-typescript traktuje TypeScript jak JavaScript z dodatkową składnią — usuwa typy bez ich weryfikacji. Jest znacznie szybszy, bo nie wykonuje pełnej kompilacji TS. Wymaga @babel/preset-env, @babel/preset-react i @babel/preset-typescript. Wadą jest brak sprawdzania typów w czasie testów — błędy typów wykryjesz dopiero przez osobne tsc --noEmit. Tej ścieżki używa domyślnie Create React App oraz next/jest.
| Cecha | ts-jest |
Babel + preset-typescript |
|---|---|---|
| Sprawdzanie typów | Tak | Nie (tylko usuwa typy) |
| Wydajność | Wolniejsze | Szybsze |
| Konfiguracja | Mniej kroków | Wymaga babel.config |
| Source maps | Dobre | Bardzo dobre |
Wsparcie dla const enum |
Tak | Nie (bez plugin) |
| Wsparcie decoratorów | Pełne | Eksperymentalne |
| Dekrety dekoratorów | Tak (z tsconfig) | Wymaga pluginu |
| Używane w CRA | Nie | Tak |
| Używane w Next.js | Nie | Tak (SWC) |
Praktyczna rekomendacja: dla bibliotek i projektów wymagających ścisłej typizacji wybierz ts-jest. Dla aplikacji frontendowych z dużą liczbą testów, gdzie liczy się szybkość, użyj Babela i osobno uruchamiaj tsc --noEmit w CI dla sprawdzania typów. W projektach Next.js i CRA decyzja jest już podjęta — używaj Babela/SWC.
Przykład kodu:
// === OPCJA 1: ts-jest ===
// npm install --save-dev ts-jest @types/jest
// jest.config.js
const { createDefaultPreset } = require('ts-jest');
module.exports = {
// Nowoczesna konfiguracja ts-jest (v29+)
...createDefaultPreset(),
testEnvironment: 'jsdom',
// Opcjonalna konfiguracja per-transform
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: 'tsconfig.test.json',
// Wyłączenie sprawdzania typów dla szybkości
isolatedModules: false,
// Diagnostyka - wyświetlanie błędów TS
diagnostics: true,
},
],
},
};
// === OPCJA 2: Babel ===
// npm install --save-dev @babel/preset-env @babel/preset-react @babel/preset-typescript babel-jest
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
['@babel/preset-react', { runtime: 'automatic' }],
'@babel/preset-typescript', // Usuwa typy, nie sprawdza
],
};
// jest.config.js dla Babela
module.exports = {
testEnvironment: 'jsdom',
// Babel jest używany automatycznie przez babel-jest
transform: {
'^.+\\.(t|j)sx?$': 'babel-jest',
},
};
// package.json - osobne sprawdzanie typów w CI
{
"scripts": {
"test": "jest",
"type-check": "tsc --noEmit",
"ci": "npm run type-check && npm test"
}
}
Materiały
Jak używać setupFiles oraz setupFilesAfterEach i czym się różnią?
Odpowiedź w 30 sekund:
setupFiles uruchamia się przed załadowaniem frameworka testowego — używany do polyfilli i konfiguracji środowiska. setupFilesAfterEach (właściwa nazwa to setupFilesAfterEach) uruchamia się po załadowaniu frameworka — używany do globalnych matcherów (jest-dom), mocków i hooków beforeEach/afterEach.
Odpowiedź w 2 minuty:
Jest oferuje kilka opcji konfiguracyjnych dla skryptów uruchamianych przed testami, a każda działa na innym etapie cyklu życia. setupFiles to tablica skryptów uruchamianych przed inicjalizacją frameworka testowego w każdym pliku testowym. Na tym etapie nie są jeszcze dostępne globalne API Jest jak expect, beforeEach, jest.mock — używamy go więc dla polyfilli (whatwg-fetch, intersection-observer), ustawiania zmiennych środowiskowych, czy konfiguracji globalnych obiektów.
setupFilesAfterEach (uwaga: właściwa nazwa to setupFilesAfterEach) działa po załadowaniu frameworka. Mamy tu dostęp do całego API Jest — można rejestrować globalne hooki beforeAll/beforeEach/afterEach/afterAll, rozszerzać expect o customowe matchery (np. @testing-library/jest-dom), konfigurować Testing Library (np. configure({ asyncUtilTimeout: 5000 })) oraz mockować moduły globalne.
Dodatkowo Jest oferuje globalSetup i globalTeardown — pliki uruchamiane raz przed/po całej suite testowej (nie per plik). Używane do startu bazy danych, generowania danych testowych, czy uruchomienia serwera mockowego.
| Opcja | Kiedy | Dostęp do API Jest | Typowe użycie |
|---|---|---|---|
globalSetup |
Raz przed wszystkimi testami | Nie | Start DB, init serwera |
setupFiles |
Przed frameworkiem, per plik | Nie | Polyfille, env vars |
setupFilesAfterEach |
Po frameworku, per plik | Tak | jest-dom, globalne mocki |
globalTeardown |
Raz po wszystkich testach | Nie | Cleanup DB, stop serwera |
Przykład kodu:
// jest.config.js
module.exports = {
// Uruchamiane RAZ przed wszystkimi testami
globalSetup: '<rootDir>/jest.global-setup.ts',
globalTeardown: '<rootDir>/jest.global-teardown.ts',
// Uruchamiane PRZED frameworkiem, dla każdego pliku testowego
setupFiles: ['<rootDir>/jest.polyfills.ts'],
// Uruchamiane PO frameworku, dla każdego pliku testowego
setupFilesAfterEach: ['<rootDir>/jest.setup.ts'],
};
// === jest.polyfills.ts - setupFiles ===
// Tu NIE MAMY dostępu do API Jest (expect, beforeEach, jest.mock)
// Tylko polyfille i konfiguracja środowiska
// Polyfill dla fetch w środowisku jsdom
import 'whatwg-fetch';
// Polyfill dla IntersectionObserver
import 'intersection-observer';
// Ustawianie zmiennych środowiskowych
process.env.API_URL = 'http://localhost:3000';
process.env.NODE_ENV = 'test';
// Globalna konfiguracja - np. timezone
process.env.TZ = 'UTC';
// === jest.setup.ts - setupFilesAfterEach ===
// Tu MAMY pełen dostęp do API Jest
import '@testing-library/jest-dom'; // Rozszerza expect o matchery DOM
import { configure } from '@testing-library/react';
import { server } from './mocks/server';
// Konfiguracja Testing Library
configure({ asyncUtilTimeout: 5000 });
// Globalne hooki dla wszystkich testów
beforeAll(() => {
// Start MSW (Mock Service Worker) przed wszystkimi testami
server.listen({ onUnhandledRequest: 'error' });
});
afterEach(() => {
// Reset handlerów po każdym teście
server.resetHandlers();
// Czyszczenie mocków
jest.clearAllMocks();
});
afterAll(() => {
// Zatrzymanie MSW po wszystkich testach
server.close();
});
// Mockowanie globalnego obiektu window.matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
})),
});
// === jest.global-setup.ts - uruchamia się RAZ ===
module.exports = async () => {
// Np. start testowej bazy danych
console.log('Uruchamianie środowiska testowego...');
// await startTestDatabase();
};
Materiały
Asercje i Matchery
Jakie są najczęściej używane matchery w Jest (toBe, toEqual, toContain, toMatch)?
Odpowiedź w 30 sekund:
Jest oferuje bogaty zestaw matcherów do weryfikacji wartości. Najczęściej używane to toBe() dla porównań prymitywów przez referencję, toEqual() dla głębokiego porównania obiektów i tablic, toContain() do sprawdzania zawartości tablic lub ciągów oraz toMatch() do dopasowania ciągów do wyrażenia regularnego lub podciągu.
Odpowiedź w 2 minuty:
Matchery (ang. matchers) to metody dołączone do obiektu zwracanego przez expect(), które pozwalają opisać oczekiwany wynik testu. Każdy matcher wykonuje konkretny rodzaj porównania, a w razie niezgodności generuje czytelny komunikat o błędzie. Dzięki temu nie musimy pisać własnych warunków if ani rzucać błędów ręcznie — Jest robi to za nas.
Najpopularniejsze matchery to: toBe() (porównanie przez Object.is, idealne dla liczb, stringów, booleanów), toEqual() (rekurencyjne porównanie struktury — używane do obiektów i tablic), toContain() (sprawdza czy element jest w tablicy lub czy podciąg występuje w stringu), toMatch() (porównuje string z regexem lub innym stringiem), toBeTruthy() / toBeFalsy() (sprawdzają wartość logiczną), toBeNull(), toBeUndefined(), toBeDefined() (sprawdzają wartości specjalne), toBeGreaterThan() / toBeLessThan() (porównania liczbowe) oraz toHaveLength() (długość tablicy/stringa).
Każdy matcher można zanegować przez .not, np. expect(value).not.toBe(5). Dla operacji asynchronicznych dostępne są dodatkowe modyfikatory .resolves i .rejects. Dobrym zwyczajem jest wybieranie najbardziej specyficznego matchera dla danej sytuacji — daje to lepsze komunikaty błędów i czytelniejsze testy.
Przykład kodu:
// toBe - porównanie przez referencję (Object.is)
test('toBe dla wartości prymitywnych', () => {
expect(2 + 2).toBe(4);
expect('hello').toBe('hello');
expect(true).toBe(true);
});
// toEqual - głębokie porównanie struktury
test('toEqual dla obiektów i tablic', () => {
const uzytkownik = { imie: 'Anna', wiek: 30 };
expect(uzytkownik).toEqual({ imie: 'Anna', wiek: 30 });
const liczby = [1, 2, 3];
expect(liczby).toEqual([1, 2, 3]);
});
// toContain - sprawdzenie zawartości
test('toContain dla tablic i stringów', () => {
const owoce = ['jablko', 'banan', 'gruszka'];
expect(owoce).toContain('banan');
const tekst = 'Witaj w Jest';
expect(tekst).toContain('Jest');
});
// toMatch - dopasowanie do regexa lub podciągu
test('toMatch dla stringów', () => {
expect('team').not.toMatch(/I/);
expect('Christoph').toMatch(/stop/);
expect('numer-123').toMatch(/\d+/);
});
// Negacja przez .not
test('uzycie .not', () => {
expect(5).not.toBe(10);
expect([1, 2, 3]).not.toContain(4);
});
Materiały
Jaka jest różnica między toBe(), toEqual() oraz toStrictEqual()?
Odpowiedź w 30 sekund:
toBe() porównuje wartości przez referencję (Object.is), działa dobrze dla prymitywów. toEqual() wykonuje głębokie porównanie struktury i ignoruje undefined w polach obiektu. toStrictEqual() jest najściślejszy — sprawdza również typ obiektu (klasę) oraz obecność pól z wartością undefined.
Odpowiedź w 2 minuty:
Wybór odpowiedniego matchera do porównania równości jest jednym z najczęstszych źródeł pomyłek w testach Jest. Trzy najważniejsze opcje to toBe(), toEqual() i toStrictEqual(), a różnice między nimi mają realny wpływ na precyzję testów.
toBe() używa Object.is() do porównania. Dla prymitywów (liczby, stringi, booleany) działa zgodnie z intuicją, ale dla obiektów i tablic porówna jedynie referencje — dwa różne obiekty o tej samej zawartości nie będą równe. Dlatego do struktur danych używamy toEqual(), który wykonuje rekurencyjne porównanie pól.
toEqual() ignoruje jednak pola z wartością undefined i nie sprawdza typu obiektu — instancja klasy User z polami {name: 'Anna'} będzie równa zwykłemu obiektowi {name: 'Anna'}. Jeśli potrzebujemy najściślejszej kontroli, używamy toStrictEqual(), który dodatkowo weryfikuje że oba obiekty są instancjami tej samej klasy (sprawdza prototyp) oraz rozróżnia {a: undefined} od {}. Jest też świadomy elementów rzadkich tablic — [, , 1] nie będzie równe [undefined, undefined, 1].
| Matcher | Mechanizm | Obiekty | Typ/klasa | Pola undefined |
|---|---|---|---|---|
toBe() |
Object.is |
tylko referencja | nie sprawdza | nie dotyczy |
toEqual() |
głębokie porównanie | po wartości | ignoruje | ignoruje |
toStrictEqual() |
głębokie + ścisłe | po wartości | sprawdza | rozróżnia |
Przykład kodu:
// toBe - referencyjne porównanie
test('toBe dla prymitywow vs obiektow', () => {
expect(42).toBe(42); // OK - prymityw
expect({ a: 1 }).not.toBe({ a: 1 }); // rozne referencje
});
// toEqual - gleboke porownanie, ale liberalne
test('toEqual ignoruje undefined i typ', () => {
expect({ a: 1, b: undefined }).toEqual({ a: 1 }); // PRZECHODZI
expect({ a: 1 }).toEqual({ a: 1, b: undefined }); // PRZECHODZI
class Uzytkownik {
constructor(imie) { this.imie = imie; }
}
expect(new Uzytkownik('Anna')).toEqual({ imie: 'Anna' }); // PRZECHODZI
});
// toStrictEqual - najscislejszy
test('toStrictEqual wykrywa subtelne roznice', () => {
// Roznica w polach undefined
expect({ a: 1, b: undefined }).not.toStrictEqual({ a: 1 });
// Roznica w klasie (prototypie)
class Uzytkownik {
constructor(imie) { this.imie = imie; }
}
expect(new Uzytkownik('Anna')).not.toStrictEqual({ imie: 'Anna' });
// Rozne instancje tej samej klasy z tymi samymi polami - OK
expect(new Uzytkownik('Anna')).toStrictEqual(new Uzytkownik('Anna'));
// Rzadkie tablice
expect([, , 1]).not.toStrictEqual([undefined, undefined, 1]);
});
Materiały
Jak testować rzucanie wyjątków za pomocą toThrow()?
Odpowiedź w 30 sekund:
Aby przetestować rzucanie wyjątków, należy przekazać do expect() funkcję (lub wrap w arrow function), a następnie użyć matchera .toThrow(). Można sprawdzić sam fakt rzucenia, konkretny komunikat (string lub regex), instancję klasy błędu albo obiekt z polami błędu.
Odpowiedź w 2 minuty:
Matcher .toThrow() (alias .toThrowError()) służy do weryfikacji, że dany kod rzuca wyjątek. Kluczowa zasada: kod, który może rzucić wyjątek, musi być przekazany do expect() jako funkcja — w przeciwnym razie wyjątek zostanie rzucony zanim Jest zdąży go przechwycić, a test się wywali.
.toThrow() przyjmuje opcjonalny argument, który pozwala na precyzyjną weryfikację: bez argumentu sprawdza tylko, że dowolny wyjątek został rzucony; jako string — czy komunikat błędu zawiera dany podciąg; jako wyrażenie regularne — czy komunikat pasuje do wzorca; jako klasa błędu (np. TypeError) — czy rzucono instancję tej klasy; jako instancja błędu — czy komunikat jest dokładnie taki sam; jako obiekt — porównuje wybrane pola błędu.
W przypadku funkcji asynchronicznych używamy .rejects.toThrow() w połączeniu z await. Można też testować negację przez .not.toThrow() — przydatne, gdy chcemy potwierdzić, że konkretne wywołanie jest bezpieczne. W praktyce warto testować zarówno typ wyjątku, jak i jego treść, aby mieć pewność, że obsługujemy właściwy przypadek brzegowy.
Przykład kodu:
function podziel(a, b) {
if (b === 0) {
throw new Error('Nie mozna dzielic przez zero');
}
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Argumenty musza byc liczbami');
}
return a / b;
}
test('rzuca jakikolwiek wyjatek', () => {
// WAZNE: przekazujemy funkcje, nie wynik wywolania!
expect(() => podziel(10, 0)).toThrow();
});
test('sprawdzenie komunikatu jako string (substring)', () => {
expect(() => podziel(10, 0)).toThrow('dzielic przez zero');
});
test('sprawdzenie komunikatu przez regex', () => {
expect(() => podziel(10, 0)).toThrow(/przez zero$/);
});
test('sprawdzenie klasy bledu', () => {
expect(() => podziel('a', 2)).toThrow(TypeError);
});
test('sprawdzenie instancji bledu', () => {
expect(() => podziel(10, 0)).toThrow(
new Error('Nie mozna dzielic przez zero')
);
});
test('kod, ktory nie rzuca wyjatku', () => {
expect(() => podziel(10, 2)).not.toThrow();
});
// Funkcje asynchroniczne
async function pobierzDane(id) {
if (!id) throw new Error('Brak ID');
return { id };
}
test('async - rzucanie wyjatku', async () => {
await expect(pobierzDane(null)).rejects.toThrow('Brak ID');
});
Materiały
Czym są asymmetric matchery (expect.any, expect.arrayContaining, expect.objectContaining)?
Odpowiedź w 30 sekund:
Asymetryczne matchery to specjalne obiekty, których używamy wewnątrz toEqual() lub innych matcherów do częściowego/elastycznego dopasowania zamiast dokładnej równości. Pozwalają porównać tylko wybrane pola obiektu (expect.objectContaining), sprawdzić obecność elementów w tablicy (expect.arrayContaining) lub typ wartości (expect.any).
Odpowiedź w 2 minuty: Asymetryczne matchery (ang. asymmetric matchers) są jednym z najpotężniejszych narzędzi w Jest do testowania struktur danych, gdy nie znamy lub nie chcemy znać wszystkich wartości z góry. Nazwa "asymetryczne" wynika z tego, że nie są one porównaniem dwóch konkretnych wartości — to obiekty, które same definiują warunek dopasowania.
Najczęściej używane to: expect.any(Constructor) — sprawdza, czy wartość jest instancją danego typu (Number, String, Function, klasa); expect.anything() — pasuje do wszystkiego oprócz null i undefined; expect.objectContaining(obj) — sprawdza, czy obiekt zawiera dane pola (pozostałe pola mogą istnieć i mieć dowolne wartości); expect.arrayContaining(arr) — sprawdza, czy tablica zawiera wszystkie wymienione elementy w dowolnej kolejności; expect.stringContaining(str) i expect.stringMatching(regex) — częściowe dopasowanie stringów.
Te matchery można dowolnie zagnieżdżać — np. objectContaining może mieć pole opisane przez arrayContaining, które z kolei zawiera expect.any(Object). Szczególnie przydają się przy testowaniu wywołań mocków (toHaveBeenCalledWith), gdzie często chcemy sprawdzić tylko fragment przekazanego argumentu, oraz przy testowaniu odpowiedzi API, gdzie pola jak id czy createdAt są generowane dynamicznie.
Przykład kodu:
// expect.any - sprawdzenie typu
test('expect.any sprawdza typ wartosci', () => {
function uzytkownik(imie) {
return { id: Math.random(), imie, utworzono: new Date() };
}
expect(uzytkownik('Anna')).toEqual({
id: expect.any(Number),
imie: 'Anna',
utworzono: expect.any(Date),
});
});
// expect.objectContaining - czesciowe dopasowanie obiektu
test('expect.objectContaining ignoruje dodatkowe pola', () => {
const odpowiedz = {
status: 200,
dane: { imie: 'Anna', wiek: 30 },
timestamp: Date.now(),
};
expect(odpowiedz).toEqual(
expect.objectContaining({
status: 200,
dane: expect.objectContaining({ imie: 'Anna' }),
})
);
});
// expect.arrayContaining - elementy w dowolnej kolejnosci
test('expect.arrayContaining sprawdza obecnosc elementow', () => {
const tagi = ['javascript', 'jest', 'testing', 'node'];
expect(tagi).toEqual(
expect.arrayContaining(['jest', 'javascript'])
);
});
// expect.stringMatching - dopasowanie stringa
test('expect.stringMatching z regexem', () => {
const email = 'anna@example.com';
expect(email).toEqual(expect.stringMatching(/^.+@.+\..+$/));
});
// Uzycie z mockami
test('asymmetric matchery z mockami', () => {
const mockFn = jest.fn();
mockFn({ uzytkownik: 'Anna', timestamp: Date.now() });
expect(mockFn).toHaveBeenCalledWith(
expect.objectContaining({
uzytkownik: 'Anna',
timestamp: expect.any(Number),
})
);
});
// Zagniezdzanie matcherow
test('zagniezdzone asymmetric matchery', () => {
const dane = {
uzytkownicy: [
{ id: 1, imie: 'Anna' },
{ id: 2, imie: 'Jan' },
],
};
expect(dane).toEqual({
uzytkownicy: expect.arrayContaining([
expect.objectContaining({ imie: 'Anna' }),
]),
});
});
Materiały
Jak tworzyć własne matchery w Jest (expect.extend)?
Odpowiedź w 30 sekund:
Własne matchery tworzymy przez expect.extend({ nazwaMatchera(received, ...args) { ... } }). Funkcja matchera musi zwrócić obiekt z polami pass (boolean) oraz message (funkcja zwracająca string komunikatu błędu). Po rejestracji można używać expect(x).nazwaMatchera().
Odpowiedź w 2 minuty:
Mimo bogatego zestawu wbudowanych matcherów, czasem potrzebujemy testować specyficzne reguły domenowe — np. że obiekt jest prawidłowym numerem PESEL, że data mieści się w danym zakresie, albo że odpowiedź API ma określoną strukturę. Jest pozwala rozszerzać API expect przez expect.extend().
Funkcja matchera otrzymuje jako pierwszy argument received (wartość przekazaną do expect()), a kolejne argumenty to wartości podane przy wywołaniu matchera. Musi zwrócić obiekt z dwoma polami: pass (czy test przeszedł) oraz message (funkcja zwracająca komunikat błędu — jest wywoływana tylko wtedy, gdy test się nie powiódł lub gdy używamy .not). Wewnątrz matchera dostępny jest też kontekst this, który zawiera m.in. this.isNot (czy użyto .not), this.utils (helpery do formatowania) i this.equals (funkcja głębokiego porównania).
Dobrze napisany matcher: ma znaczącą nazwę (toBeValidPesel, a nie customMatcher1), zwraca czytelny komunikat błędu z informacją o oczekiwaniu i otrzymanej wartości, używa this.utils.printReceived/printExpected do formatowania oraz obsługuje przypadek .not (komunikat różni się zależnie od this.isNot). Matchery można też pisać asynchronicznie — wystarczy zwrócić Promise z obiektem {pass, message}. W projekcie TypeScript warto dodatkowo zadeklarować typy w pliku .d.ts, aby IDE rozpoznawało nowe metody.
Przykład kodu:
// Prosty matcher sprawdzajacy zakres liczbowy
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
if (pass) {
return {
message: () =>
`oczekiwano, ze ${received} NIE bedzie w zakresie ${floor}-${ceiling}`,
pass: true,
};
} else {
return {
message: () =>
`oczekiwano, ze ${received} bedzie w zakresie ${floor}-${ceiling}`,
pass: false,
};
}
},
});
test('uzycie wlasnego matchera', () => {
expect(50).toBeWithinRange(10, 100);
expect(5).not.toBeWithinRange(10, 100);
});
// Bardziej zaawansowany matcher z this.utils
expect.extend({
toBeValidPesel(received) {
const isString = typeof received === 'string';
const hasCorrectLength = isString && received.length === 11;
const isNumeric = isString && /^\d+$/.test(received);
const pass = hasCorrectLength && isNumeric;
return {
pass,
message: () =>
this.isNot
? `oczekiwano, ze ${this.utils.printReceived(received)} NIE bedzie prawidlowym PESEL`
: `oczekiwano, ze ${this.utils.printReceived(received)} bedzie prawidlowym PESEL (11 cyfr)`,
};
},
});
test('walidacja PESEL', () => {
expect('12345678901').toBeValidPesel();
expect('abc').not.toBeValidPesel();
expect('123').not.toBeValidPesel();
});
// Asynchroniczny matcher
expect.extend({
async toResolveWithStatus(receivedPromise, expectedStatus) {
const response = await receivedPromise;
const pass = response.status === expectedStatus;
return {
pass,
message: () =>
`oczekiwano statusu ${expectedStatus}, otrzymano ${response.status}`,
};
},
});
test('async matcher', async () => {
const promise = Promise.resolve({ status: 200 });
await expect(promise).toResolveWithStatus(200);
});
W projekcie TypeScript dodajemy typy w jest.d.ts:
declare global {
namespace jest {
interface Matchers<R> {
toBeWithinRange(floor: number, ceiling: number): R;
toBeValidPesel(): R;
}
}
}
Materiały
Mockowanie - jest.fn i jest.mock
Czym jest mock i kiedy warto go używać w testach?
Odpowiedź w 30 sekund: Mock to obiekt zastępczy, który imituje zachowanie prawdziwej zależności (funkcji, modułu, klasy) w testach. Używamy go, gdy chcemy odizolować testowany kod od zewnętrznych zależności (API, baza danych, czas, losowość) lub gdy chcemy zweryfikować interakcje między obiektami (czy funkcja została wywołana z odpowiednimi argumentami).
Odpowiedź w 2 minuty: Mock to ogólne pojęcie z rodziny "test doubles" (zamienników testowych), które obejmuje także stuby, spies, fakes i dummies. W kontekście Jest, mock to obiekt, który zapamiętuje wszystkie swoje wywołania (argumenty, kontekst, wartości zwracane) i pozwala na kontrolowanie tego, co zwraca w trakcie testu. Dzięki temu możemy testować kod w izolacji, bez polegania na rzeczywistych implementacjach.
Kiedy warto używać mocków? Po pierwsze, gdy testujemy kod komunikujący się z zewnętrznymi serwisami (HTTP API, baza danych, system plików) – nie chcemy w testach jednostkowych wykonywać prawdziwych żądań sieciowych. Po drugie, gdy zależność jest niedeterministyczna (czas, Math.random, generator UUID) – mockowanie daje przewidywalność. Po trzecie, gdy testujemy interakcje – chcemy sprawdzić, czy nasz kod wywołał określoną funkcję z odpowiednimi argumentami (np. czy logger został wywołany przy błędzie).
Kiedy NIE używać mocków? Nadmierne mockowanie prowadzi do testów, które weryfikują implementację zamiast zachowania – każda zmiana w kodzie wymaga aktualizacji testów. Mockujemy granice systemu (I/O), a nie czystą logikę biznesową. Jeśli mockujemy 90% kodu, prawdopodobnie testujemy tylko mocki, a nie rzeczywiste zachowanie aplikacji.
Przykład kodu:
// Kod produkcyjny - serwis użytkownika
class UserService {
constructor(httpClient, logger) {
this.httpClient = httpClient;
this.logger = logger;
}
async fetchUser(id) {
try {
const response = await this.httpClient.get(`/api/users/${id}`);
return response.data;
} catch (error) {
this.logger.error('Nie udało się pobrać użytkownika', error);
throw error;
}
}
}
// Test z użyciem mocków - izolacja od HTTP i loggera
test('pobiera użytkownika i zwraca dane', async () => {
// Mock zależności - zastępujemy prawdziwy HTTP klient i logger
const mockHttpClient = {
get: jest.fn().mockResolvedValue({ data: { id: 1, name: 'Anna' } })
};
const mockLogger = { error: jest.fn() };
const service = new UserService(mockHttpClient, mockLogger);
const user = await service.fetchUser(1);
// Weryfikujemy zachowanie - czy zostały wywołane odpowiednie metody
expect(user).toEqual({ id: 1, name: 'Anna' });
expect(mockHttpClient.get).toHaveBeenCalledWith('/api/users/1');
expect(mockLogger.error).not.toHaveBeenCalled();
});
Materiały
Jak tworzyć mock funkcji za pomocą jest.fn() i sprawdzać jej wywołania?
Odpowiedź w 30 sekund:
jest.fn() tworzy nową funkcję mockującą, która zapamiętuje wszystkie wywołania w obiekcie .mock. Możemy kontrolować jej zachowanie metodami mockReturnValue, mockResolvedValue, mockImplementation, a następnie weryfikować wywołania matcherami toHaveBeenCalled, toHaveBeenCalledWith, toHaveBeenCalledTimes.
Odpowiedź w 2 minuty:
jest.fn() to fabryka funkcji mockujących. Wywołanie bez argumentów tworzy funkcję, która zwraca undefined i niczego nie robi, ale rejestruje wszystkie swoje wywołania. Każda taka funkcja ma właściwość .mock, która zawiera m.in. calls (tablica argumentów wszystkich wywołań), results (zwrócone wartości lub rzucone wyjątki), instances (kontekst this) i contexts.
Konfiguracja zachowania mocka odbywa się przez metody łańcuchowe:
mockReturnValue(value)– synchroniczna wartość zwracanamockResolvedValue(value)– Promise.resolve dla testów asynchronicznychmockRejectedValue(error)– Promise.reject dla testowania błędówmockImplementation(fn)– dowolna implementacjamockReturnValueOnce(value)– wartość tylko dla pierwszego wywołania (kolejkowanie)
Weryfikacja wywołań używa matcherów Jest:
toHaveBeenCalled()– czy w ogóle wywołanetoHaveBeenCalledTimes(n)– ile razytoHaveBeenCalledWith(arg1, arg2)– z jakimi argumentamitoHaveBeenLastCalledWith(...)– ostatnie wywołanietoHaveBeenNthCalledWith(n, ...)– n-te wywołanie
Pamiętaj o czyszczeniu mocków między testami przez jest.clearAllMocks() w beforeEach lub konfigurację clearMocks: true w jest.config.js, inaczej stan z poprzednich testów może wpływać na asercje.
Przykład kodu:
test('jest.fn() - tworzenie i weryfikacja mocka', () => {
// Tworzenie prostego mocka
const callback = jest.fn();
// Wywołanie mocka kilka razy z różnymi argumentami
callback('Anna', 25);
callback('Jan', 30);
// Weryfikacja wywołań
expect(callback).toHaveBeenCalled();
expect(callback).toHaveBeenCalledTimes(2);
expect(callback).toHaveBeenCalledWith('Anna', 25);
expect(callback).toHaveBeenLastCalledWith('Jan', 30);
expect(callback).toHaveBeenNthCalledWith(1, 'Anna', 25);
// Dostęp do surowych danych o wywołaniach
expect(callback.mock.calls).toEqual([['Anna', 25], ['Jan', 30]]);
});
test('konfiguracja wartości zwracanych', async () => {
// mockReturnValue - synchroniczne wartości
const sumator = jest.fn().mockReturnValue(42);
expect(sumator()).toBe(42);
// mockReturnValueOnce - kolejkowanie wartości
const licznik = jest.fn()
.mockReturnValueOnce(1)
.mockReturnValueOnce(2)
.mockReturnValue(99); // domyślna wartość dla pozostałych
expect(licznik()).toBe(1);
expect(licznik()).toBe(2);
expect(licznik()).toBe(99);
expect(licznik()).toBe(99);
// mockResolvedValue - dla funkcji asynchronicznych
const pobierzDane = jest.fn().mockResolvedValue({ id: 1 });
await expect(pobierzDane()).resolves.toEqual({ id: 1 });
// mockImplementation - pełna kontrola nad zachowaniem
const mnożenie = jest.fn().mockImplementation((a, b) => a * b);
expect(mnożenie(3, 4)).toBe(12);
});
Materiały
Jaka jest różnica między jest.fn(), jest.spyOn() a jest.mock()?
Odpowiedź w 30 sekund:
jest.fn() tworzy nową, samodzielną funkcję mockującą. jest.spyOn(obj, 'metoda') "podsłuchuje" istniejącą metodę obiektu – domyślnie zachowuje oryginalną implementację, ale rejestruje wywołania. jest.mock('moduł') zastępuje cały moduł automatycznie wygenerowanym (lub ręcznym) mockiem – to operacja na poziomie modułu, nie pojedynczej funkcji.
Odpowiedź w 2 minuty: To trzy różne narzędzia o różnym zasięgu i przeznaczeniu. Wybór zależy od tego, co i jak chcemy zastąpić.
jest.fn() tworzy izolowaną funkcję mockującą "z powietrza" – używamy jej do przekazania jako callback, dependency injection lub do budowania mock obiektów. Nie modyfikuje żadnego istniejącego kodu, jest samodzielna.
jest.spyOn(obiekt, 'nazwaMetody') to mock zachowujący oryginał. Tworzy "szpiega" – metoda nadal działa normalnie, ale wszystkie wywołania są rejestrowane. Idealna do weryfikacji efektów ubocznych bez zmiany zachowania. Można ją łączyć z mockImplementation() lub mockReturnValue() żeby zmienić zachowanie. Ważne: spy MUSI być przywrócony przez mockRestore() po teście, inaczej zmiana pozostaje globalna i wpływa na kolejne testy.
jest.mock('ścieżka/do/modułu') działa na poziomie modułu – zastępuje cały import. Automatycznie generuje mock dla wszystkich eksportów (funkcje stają się jest.fn() zwracającymi undefined). Można podać drugi argument – factory funkcję zwracającą custom mock. Wywołanie jest.mock() jest hoistowane na początek pliku przez Jest babel transform.
| Cecha | jest.fn() |
jest.spyOn() |
jest.mock() |
|---|---|---|---|
| Zasięg | Pojedyncza funkcja | Metoda obiektu | Cały moduł |
| Tworzy nowy mock | Tak | Nie - opakowuje istniejący | Tak - dla całego modułu |
| Zachowuje oryginał | Nie | Tak (domyślnie) | Nie (domyślnie) |
Wymaga mockRestore() |
Nie | Tak | Nie (auto przy restoreMocks) |
| Hoisting | Nie | Nie | Tak (na początek pliku) |
| Typowe użycie | Callback, DI | Podsłuch metod, console.log | Mock całych modułów |
Przykład kodu:
// 1. jest.fn() - samodzielny mock jako callback
test('jest.fn() jako callback', () => {
const mockCallback = jest.fn(x => x * 2);
[1, 2, 3].forEach(mockCallback);
expect(mockCallback).toHaveBeenCalledTimes(3);
expect(mockCallback.mock.results[0].value).toBe(2);
});
// 2. jest.spyOn() - podsłuchiwanie istniejącej metody
test('jest.spyOn() - weryfikacja bez zmiany zachowania', () => {
const użytkownik = {
powitaj(imie) { return `Witaj, ${imie}!`; }
};
const spy = jest.spyOn(użytkownik, 'powitaj');
const wynik = użytkownik.powitaj('Anna');
// Oryginalna metoda nadal działa, ale wywołanie jest zarejestrowane
expect(wynik).toBe('Witaj, Anna!');
expect(spy).toHaveBeenCalledWith('Anna');
spy.mockRestore(); // KLUCZOWE - przywróć oryginał
});
test('jest.spyOn() z podmianą implementacji', () => {
// Częsty przypadek - wyciszenie console.error w testach
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
console.error('Ten błąd nie zostanie wypisany');
expect(errorSpy).toHaveBeenCalledWith('Ten błąd nie zostanie wypisany');
errorSpy.mockRestore();
});
// 3. jest.mock() - mockowanie całego modułu
jest.mock('./apiClient'); // Hoistowane na górę pliku!
import { pobierzUżytkownika } from './apiClient';
test('jest.mock() - cały moduł zastąpiony', async () => {
pobierzUżytkownika.mockResolvedValue({ id: 1, name: 'Anna' });
const dane = await pobierzUżytkownika(1);
expect(dane).toEqual({ id: 1, name: 'Anna' });
expect(pobierzUżytkownika).toHaveBeenCalledWith(1);
});
Materiały
Jak mockować cały moduł za pomocą jest.mock() i __mocks__?
Odpowiedź w 30 sekund:
jest.mock('./moduł') automatycznie zastępuje moduł – wszystkie eksporty stają się funkcjami mockującymi. Można podać factory function jako drugi argument dla custom implementacji. Folder __mocks__ obok mockowanego pliku pozwala na trwałe ręczne mocki (manual mocks) używane w wielu testach.
Odpowiedź w 2 minuty: Jest oferuje trzy poziomy mockowania modułów, od najprostszego do najbardziej zaawansowanego.
1. Automatyczny mock (jest.mock('./ścieżka')): Jest sam analizuje moduł i tworzy zamiennik – funkcje stają się jest.fn() zwracającymi undefined, klasy są auto-mockowane (konstruktor + metody to mocki), prymitywy zachowują wartości. To wystarczy, gdy nie zależy nam na zwracanych wartościach, a tylko chcemy zweryfikować wywołania.
2. Mock z factory function: jest.mock('./moduł', () => ({ ... })) daje pełną kontrolę – możemy zwrócić dowolną strukturę. UWAGA: factory nie ma dostępu do zmiennych ze scope'u (przez hoisting), z wyjątkiem zmiennych z prefiksem mock (np. mockFoo). To częsta pułapka.
3. Manual mocks w folderze __mocks__: Tworzymy folder __mocks__ obok mockowanego modułu (lub w korzeniu projektu dla node_modules). Plik wewnątrz definiuje stałą implementację mocka. Aby aktywować, wywołujemy jest.mock('./moduł') bez factory – Jest automatycznie podstawi __mocks__/moduł.js. Dla modułów z node_modules (np. lodash, axios) mock w __mocks__/lodash.js w korzeniu jest aktywowany automatycznie (lub przez jest.mock('lodash') w teście).
Częściowy mock: jest.mock('./moduł', () => ({ ...jest.requireActual('./moduł'), specyficznaFunkcja: jest.fn() })) pozwala zachować większość modułu i zmockować tylko wybrane funkcje – bardzo użyteczne dla bibliotek narzędziowych.
| Strategia | Kiedy używać | Konfiguracja |
|---|---|---|
| Auto mock | Testy, gdzie wartości zwracane nie mają znaczenia | jest.mock('./moduł') |
| Factory mock | Custom zachowanie dla jednego testu | jest.mock('./moduł', () => ({...})) |
Manual mock __mocks__ |
Wielokrotne użycie tego samego mocka | Plik w __mocks__/nazwa.js |
| Częściowy mock | Mock tylko fragmentu modułu | jest.requireActual + spread |
Przykład kodu:
// 1. AUTO MOCK - cały moduł automatycznie zmockowany
// plik: services/database.js
export const zapiszUżytkownika = (user) => { /* prawdziwa logika */ };
export const pobierzUżytkownika = (id) => { /* prawdziwa logika */ };
// plik: user.test.js
import { zapiszUżytkownika, pobierzUżytkownika } from './services/database';
jest.mock('./services/database'); // wszystko staje się jest.fn()
test('automatyczny mock modułu', () => {
pobierzUżytkownika.mockReturnValue({ id: 1, name: 'Anna' });
expect(pobierzUżytkownika(1)).toEqual({ id: 1, name: 'Anna' });
expect(zapiszUżytkownika).not.toHaveBeenCalled();
});
// 2. FACTORY MOCK - custom implementacja
jest.mock('./services/api', () => ({
fetchData: jest.fn(() => Promise.resolve({ success: true })),
postData: jest.fn(() => Promise.resolve({ id: 123 })),
// PUŁAPKA: nie możesz odwołać się do zewnętrznych zmiennych!
// const dane = { foo: 'bar' }; - to NIE zadziała przez hoisting
}));
// 3. MANUAL MOCK w __mocks__
// Struktura folderów:
// project/
// services/
// api.js
// __mocks__/
// api.js <- mock automatycznie używany
// user.test.js
// services/__mocks__/api.js
export const fetchData = jest.fn(() =>
Promise.resolve({ użytkownicy: [{ id: 1, name: 'Anna' }] })
);
export const postData = jest.fn(() => Promise.resolve({ id: 999 }));
// W teście wystarczy:
jest.mock('./services/api'); // użyje __mocks__/api.js
// 4. CZĘŚCIOWY MOCK - zmockuj tylko wybrane funkcje
jest.mock('./utils/format', () => {
const oryginalny = jest.requireActual('./utils/format');
return {
...oryginalny, // zachowaj wszystkie oryginalne funkcje
formatujDate: jest.fn(() => '2024-01-01'), // zmockuj tylko jedną
};
});
// 5. MOCK BIBLIOTEKI Z NODE_MODULES
// Plik w korzeniu: __mocks__/axios.js
export default {
get: jest.fn(() => Promise.resolve({ data: {} })),
post: jest.fn(() => Promise.resolve({ data: {} })),
};
// W teście:
jest.mock('axios'); // automatycznie podstawi __mocks__/axios.js
Materiały
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziimport) i pułapki związane z hoistingiem jest.mock()?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziSpies i Reset Mocków
jest.spyOn() zamiast jest.fn()?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzimockClear, mockReset a mockRestore?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziclearMocks, resetMocks i restoreMocks w konfiguracji?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziTesty Asynchroniczne
async/await i Promises?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziresolves i rejects w asercjach asynchronicznych?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzidone i jakie są tego pułapki?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzifetch oraz wywołania API w Jest?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzisetTimeout, setInterval) za pomocą jest.useFakeTimers()?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziHooks i Lifecycle
beforeEach, afterEach, beforeAll, afterAll)?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzidescribe?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzi--testNamePattern oraz modyfikatorów test.only, test.skip, test.todo?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziSnapshot Testing
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzi--updateSnapshot, -u)?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziCode Coverage
--coverage) i co oznaczają metryki branches, statements, functions, lines?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzicoverageThreshold) i wymusić ich spełnienie w CI?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzicollectCoverageFrom i dlaczego nie wystarczy samo --coverage?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziTesty Parametryzowane i Strukturyzacja
test.each() i describe.each() do testów parametryzowanych?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzi__tests__, sąsiadujące .test.js, struktura katalogów)?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziWydajność i Debugowanie
--maxWorkers)?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzi--runInBand)?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzi--watch i jak różni się od --watchAll?
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziBest Practices i Wzorce
Odpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedziOdpowiedź dostępna w pełnej wersji. Odblokuj 35 pozostałych odpowiedzi i ucz się z pełnego zestawu.
Odblokuj odpowiedzi