Projekt Pathly wykorzystuje dwa główne frameworki testowe:
- Vitest - do testów jednostkowych i integracyjnych
- Playwright - do testów end-to-end (E2E)
# Uruchom testy w trybie watch
npm run test
# Uruchom testy z interfejsem UI
npm run test:ui
# Uruchom testy jednokrotnie
npm run test:run
# Uruchom testy z pokryciem kodu
npm run test:coveragesrc/test/
├── setup-tests.ts # Konfiguracja środowiska testowego
├── mocks/ # Mock Service Worker handlers
│ ├── handlers.ts # Definicje handlerów API
│ ├── server.ts # Server dla Node.js (Vitest)
│ └── browser.ts # Worker dla przeglądarki
├── utils/ # Funkcje pomocnicze
│ ├── test-helpers.tsx # Renderowanie z providerami
│ └── mock-data.ts # Mockowane dane testowe
└── example.test.tsx # Przykładowy test
- Pliki testowe:
*.test.ts,*.test.tsx,*.spec.ts,*.spec.tsx - Umieszczaj testy obok testowanych plików lub w katalogu
src/test/ - Używaj opisowych nazw:
Button.test.tsx,useProfile.test.ts
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { Button } from './Button';
describe('Button', () => {
it('should call onClick when clicked', async () => {
const handleClick = vi.fn();
const user = userEvent.setup();
render(<Button onClick={handleClick}>Click me</Button>);
await user.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});MSW jest skonfigurowany globalnie w setup-tests.ts. Dodaj handlery w src/test/mocks/handlers.ts:
import { http, HttpResponse } from "msw";
export const handlers = [
http.get("/api/routes", () => {
return HttpResponse.json([{ id: "1", name: "Test Route" }]);
}),
];vi.mock("next/navigation", () => ({
useRouter: vi.fn(() => ({
push: vi.fn(),
})),
}));Konfiguracja w vitest.config.ts:
- Minimalne progi: 70% dla wszystkich metryk
- Raporty: text, json, html, lcov
- Wyłączenia: node_modules, pliki konfiguracyjne, typy
# Uruchom wszystkie testy E2E
npm run test:e2e
# Uruchom testy z interfejsem UI
npm run test:e2e:ui
# Uruchom testy w trybie debugowania
npm run test:e2e:debug
# Wygeneruj kod testu z przeglądarki (codegen)
npm run test:e2e:codegen
# Pokaż raport z ostatnich testów
npm run test:e2e:reporte2e/
├── pages/ # Page Object Model
│ ├── base.page.ts # Klasa bazowa
│ └── login.page.ts # Strona logowania
├── fixtures/ # Fixtures i helpers
│ ├── helpers.ts # Funkcje pomocnicze
│ └── test-data.ts # Dane testowe
└── example.spec.ts # Przykładowy test
- Pliki testowe:
*.spec.ts - Page Objects:
*.page.ts - Umieszczaj wszystkie testy E2E w katalogu
e2e/
import { test, expect } from "@playwright/test";
test("should login successfully", async ({ page }) => {
await page.goto("/login");
await page.getByLabel(/email/i).fill("test@example.com");
await page.getByLabel(/password/i).fill("password123");
await page.getByRole("button", { name: /sign in/i }).click();
await expect(page).toHaveURL(/.*dashboard/);
});Przykład implementacji POM:
import { Page } from "@playwright/test";
import { BasePage } from "./base.page";
export class LoginPage extends BasePage {
private readonly emailInput = this.page.getByLabel(/email/i);
private readonly passwordInput = this.page.getByLabel(/password/i);
private readonly submitButton = this.page.getByRole("button", { name: /sign in/i });
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
}Plik playwright.config.ts zawiera:
- Testowanie tylko na Chromium (zgodnie z zasadami)
- Automatyczne uruchamianie dev servera
- Trace viewer dla niepowodzeń
- Screenshots i wideo przy błędach
Testy dostępności są zintegrowane z Playwright. Przykład:
import { test } from "@playwright/test";
import { injectAxe, checkA11y } from "@axe-core/playwright";
test("should not have a11y violations", async ({ page }) => {
await page.goto("/");
await injectAxe(page);
await checkA11y(page);
});-
Testuj zachowanie, nie implementację
- Unikaj testowania detali implementacyjnych
- Skup się na zachowaniu widocznym dla użytkownika
-
Używaj AAA pattern (Arrange-Act-Assert)
// Arrange const user = userEvent.setup(); render(<Component />); // Act await user.click(screen.getByRole('button')); // Assert expect(screen.getByText('Success')).toBeInTheDocument();
-
Mockuj tylko co potrzebne
- Nie mockuj wszystkiego
- Używaj MSW dla API requests
- Mockuj external dependencies
-
Pisz testy czytelne i zrozumiałe
- Opisowe nazwy testów
- Jeden assertion per test (gdy to możliwe)
- Unikaj skomplikowanej logiki w testach
-
Używaj Page Object Model
- Enkapsuluj logikę stron w klasach
- Unikaj duplikacji selektorów
-
Testuj krytyczne ścieżki użytkownika
- Logowanie/wylogowanie
- Główne funkcjonalności biznesowe
- Happy paths i podstawowe error cases
-
Używaj meaningful selectors
// Dobre ✅ page.getByRole("button", { name: /submit/i }); page.getByLabel(/email/i); // Złe ❌ page.locator(".btn-primary"); page.locator("#submit-btn");
-
Izoluj testy
- Każdy test powinien być niezależny
- Cleanup po testach
- Nie polegaj na kolejności wykonania
Gdy będziesz gotowy, możesz dodać workflow .github/workflows/tests.yml:
Zalecane joby:
unit-tests- Linting, type checking, testy jednostkowe, coveragee2e-tests- Testy Playwright z uploadem raportów
Wymagane secrets:
NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYCODECOV_TOKEN(opcjonalnie)
Upewnij się, że wszystkie moduły Next.js są poprawnie zmockowane w setup-tests.ts.
# Zainstaluj przeglądarki
npx playwright install
# Zainstaluj zależności systemowe
npx playwright install-depsSprawdź czy:
- Server jest uruchomiony w
setup-tests.ts - Handlery są poprawnie zdefiniowane
- URL w handlerach pasuje do requestów
W razie problemów:
- Sprawdź przykładowe testy w
src/test/example.test.tsxie2e/example.spec.ts - Przeczytaj dokumentację frameworków
- Uruchom testy z flagą
--reporter=verbosedla szczegółowych logów