Vitest Unit Tesztek
A Vitest egy gyors, modern unit teszt keretrendszer, amely natívan támogatja a TypeScript-et és a Vite-ot.
Alapok
Szekció neve “Alapok”Teszt futtatás
Szekció neve “Teszt futtatás”# Egyszeri futtatás (CI/CD)cd apps/web && bun test
# Watch mód (fejlesztés közben)cd apps/web && bunx vitest
# Lefedettség méréscd apps/web && bunx vitest --coverageTeszt fájl létrehozása
Szekció neve “Teszt fájl létrehozása”A teszteket a tesztelt fájl mellé helyezzük .test.ts vagy .spec.ts kiterjesztéssel:
src/lib/utils/├── format.ts└── format.test.tsPélda teszt
Szekció neve “Példa teszt”import { describe, it, expect } from 'vitest';import { formatCurrency } from './format';
describe('formatCurrency', () => { it('formázza a pénzösszeget forintban', () => { const result = formatCurrency(1234.56); expect(result).toBe('1 234,56 Ft'); });
it('kerekíti a filléreket', () => { const result = formatCurrency(1234.567); expect(result).toBe('1 234,57 Ft'); });
it('kezeli a nulla értéket', () => { const result = formatCurrency(0); expect(result).toBe('0,00 Ft'); });
it('kezeli a negatív értékeket', () => { const result = formatCurrency(-500); expect(result).toBe('-500,00 Ft'); });});Mocking
Szekció neve “Mocking”Függvény mock
Szekció neve “Függvény mock”import { vi, describe, it, expect } from 'vitest';
describe('sendEmail', () => { it('meghívja a nodemailer-t a megfelelő paraméterekkel', async () => { const mockSend = vi.fn().mockResolvedValue({ messageId: '123' });
await sendEmail({ to: 'test@example.com', subject: 'Teszt', html: '<p>Teszt üzenet</p>' });
expect(mockSend).toHaveBeenCalledWith({ to: 'test@example.com', subject: 'Teszt', html: '<p>Teszt üzenet</p>' }); });});Modul mock
Szekció neve “Modul mock”import { vi } from 'vitest';
// Teljes modul mockvi.mock('$lib/server/database/client', () => ({ db: { select: vi.fn(), insert: vi.fn(), update: vi.fn(), delete: vi.fn() }}));
// Részleges modul mockvi.mock('$lib/utils/date', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, getCurrentDate: vi.fn(() => new Date('2024-01-01')) };});Svelte komponens tesztelés
Szekció neve “Svelte komponens tesztelés”import { render, screen } from '@testing-library/svelte';import { describe, it, expect } from 'vitest';import Button from './Button.svelte';
describe('Button', () => { it('rendereli a szöveget', () => { render(Button, { props: { children: 'Kattints ide' } }); expect(screen.getByText('Kattints ide')).toBeInTheDocument(); });
it('meghívja az onclick handlert', async () => { const handleClick = vi.fn(); const { component } = render(Button, { props: { onclick: handleClick } });
await component.$set({ onclick: handleClick }); const button = screen.getByRole('button'); await button.click();
expect(handleClick).toHaveBeenCalledOnce(); });
it('disabled állapotban nem kattintható', () => { render(Button, { props: { disabled: true } }); const button = screen.getByRole('button'); expect(button).toBeDisabled(); });});Store tesztelés
Szekció neve “Store tesztelés”import { describe, it, expect, beforeEach } from 'vitest';import { createWindowManager } from '$lib/stores/window-manager.svelte';
describe('WindowManager', () => { let manager: ReturnType<typeof createWindowManager>;
beforeEach(() => { manager = createWindowManager(); });
it('új ablak megnyitása', () => { manager.openWindow({ id: 'test-1', appId: 'settings', title: 'Beállítások' });
expect(manager.windows).toHaveLength(1); expect(manager.windows[0].id).toBe('test-1'); });
it('ablak bezárása', () => { manager.openWindow({ id: 'test-1', appId: 'settings' }); manager.closeWindow('test-1');
expect(manager.windows).toHaveLength(0); });
it('aktív ablak váltás', () => { manager.openWindow({ id: 'test-1', appId: 'settings' }); manager.openWindow({ id: 'test-2', appId: 'users' });
manager.focusWindow('test-1');
expect(manager.activeWindowId).toBe('test-1'); });});Server Action tesztelés
Szekció neve “Server Action tesztelés”import { describe, it, expect, vi } from 'vitest';import { updateUserProfile } from './profile.remote';
describe('updateUserProfile', () => { it('frissíti a felhasználó profilját', async () => { // Mock request event vi.mock('$app/server', () => ({ getRequestEvent: () => ({ locals: { session: { userId: '123' } } }) }));
const result = await updateUserProfile({ name: 'Teszt Felhasználó', email: 'test@example.com' });
expect(result.success).toBe(true); expect(result.user.name).toBe('Teszt Felhasználó'); });
it('hibát dob érvénytelen email esetén', async () => { const result = await updateUserProfile({ name: 'Teszt', email: 'invalid-email' });
expect(result.success).toBe(false); expect(result.error).toContain('email'); });});Aszinkron tesztelés
Szekció neve “Aszinkron tesztelés”import { describe, it, expect } from 'vitest';
describe('fetchUserData', () => { it('lekéri a felhasználó adatait', async () => { const data = await fetchUserData('123');
expect(data).toMatchObject({ id: '123', name: expect.any(String), email: expect.any(String) }); });
it('hibát dob nem létező felhasználó esetén', async () => { await expect(fetchUserData('invalid')).rejects.toThrow('User not found'); });});Teszt adatok generálása
Szekció neve “Teszt adatok generálása”import { faker } from '@faker-js/faker';
describe('User validation', () => { it('validálja a felhasználói adatokat', () => { const testUser = { name: faker.person.fullName(), email: faker.internet.email(), password: faker.internet.password({ length: 12 }), birthDate: faker.date.past({ years: 30 }) };
const result = validateUser(testUser); expect(result.valid).toBe(true); });});Hasznos matcher-ek
Szekció neve “Hasznos matcher-ek”// Egyenlőségexpect(value).toBe(expected); // Strict equality (===)expect(value).toEqual(expected); // Deep equalityexpect(value).toStrictEqual(expected); // Strict deep equality
// Típusokexpect(value).toBeTypeOf('string');expect(value).toBeInstanceOf(Date);
// Számokexpect(value).toBeGreaterThan(10);expect(value).toBeLessThanOrEqual(100);expect(value).toBeCloseTo(0.3, 5); // Float összehasonlítás
// Stringekexpect(text).toContain('substring');expect(text).toMatch(/regex/);
// Tömbökexpect(array).toHaveLength(3);expect(array).toContain(item);expect(array).toContainEqual({ id: 1 });
// Objektumokexpect(obj).toHaveProperty('key');expect(obj).toMatchObject({ key: 'value' });
// Kivételekexpect(() => fn()).toThrow();expect(() => fn()).toThrow('Error message');
// Aszinkronawait expect(promise).resolves.toBe(value);await expect(promise).rejects.toThrow();Lifecycle hook-ok
Szekció neve “Lifecycle hook-ok”import { describe, it, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
describe('Database tests', () => { // Egyszer fut a suite előtt beforeAll(async () => { await connectDatabase(); });
// Egyszer fut a suite után afterAll(async () => { await disconnectDatabase(); });
// Minden teszt előtt fut beforeEach(async () => { await clearDatabase(); await seedTestData(); });
// Minden teszt után fut afterEach(async () => { await cleanupTestData(); });
it('teszt 1', () => { // ... });
it('teszt 2', () => { // ... });});Konfigurációs tippek
Szekció neve “Konfigurációs tippek”A vite.config.ts fájlban:
import { defineConfig } from 'vitest/config';
export default defineConfig({ test: { globals: true, environment: 'jsdom', setupFiles: ['./src/test/setup.ts'], coverage: { provider: 'v8', reporter: ['text', 'html', 'lcov'], exclude: [ 'node_modules/', 'src/test/', '**/*.test.ts', '**/*.spec.ts' ] } }});Best practice-ek
Szekció neve “Best practice-ek”- AAA minta: Arrange (előkészítés), Act (végrehajtás), Assert (ellenőrzés)
- Egy teszt, egy állítás: Lehetőleg egy dolgot tesztelj egy tesztben
- Beszédes nevek: A teszt neve mondja el, mit tesztel
- Független tesztek: Egyik teszt ne függjön a másiktól
- Mock-ok tisztítása: Minden teszt után tisztítsd meg a mock-okat
- Edge case-ek: Teszteld az üres, null, undefined eseteket
- Gyors tesztek: Unit tesztek legyenek gyorsak (< 100ms)