Praktyki Playwright, które przetrwają wzrost
Większość pakietów testów gnije z czasem. Testy stają się niestabilne, wolne i ignorowane. Poznaj praktyki, które utrzymują testy Playwright wartościowe wraz z rozwojem zespołu: własność, tagowanie.
Ten wpis jest częścią serii Podstawy Playwright. Jeśli pominąłeś poprzedni post, przeczytaj najpierw Część 9 — Debugowanie padających testów Playwright jak pro.
Wprowadzenie — wzrost to test jakości
Pakiet testów, który dobrze działa z 50 testami i 3 deweloperami, nie będzie automatycznie dobrze działać z 500 testami i 30 deweloperami. Wzrost ujawnia każdą wadę w systemie:
- Testy, które były “wystarczająco szybkie” przy 5 minutach, teraz trwają 45 minut
- Testy, które były “czasami niestabilne”, są teraz stale niestabilne
- Testy, które “wszyscy wiedzą jak naprawić”, teraz nie mają właściciela
- Testy, które “pokrywają ważne rzeczy”, już nie pokrywają tego, co ważne
Większość zespołów reaguje na to ignorowaniem testów. Padające testy są pomijane. Niestabilne testy są powtarzane, aż przejdą. CI jest traktowane jako doradcze, a nie blokujące. Pakiet testów staje się podatkiem, którego wszyscy nienawidzą płacić.
To nie musi się wydarzyć. Praktyki w tym poście utrzymają twój pakiet Playwright wartościowym, gdy twój zespół i codebase rosną. To lekcje wyciągnięte z zespołów, które utrzymywały wysokiej jakości pakiety testów przez lata, a nie miesiące.
1. Własność: każdy test ma właściciela
Pojedynczy najbardziej predykcyjny czynnik zdrowia pakietu testów to własność. Jeśli nikt nie jest właścicielem testu, nikt go nie utrzymuje. Jeśli nikt go nie utrzymuje, gnije.
Jak ustanowić własność
Taguj testy według zespołu lub domeny:
test('użytkownik może dokonać checkout', {
annotation: { type: 'owner', description: 'payments-team' },
}, async ({ page }) => {
// logika testu
});
Lub użyj własności opartej na plikach: tests/payments/ należy do payments-team, tests/auth/ należy do auth-team.
Uczyń własność widoczną w raportach: Jeśli test pada, raport CI powinien pokazać, kto jest właścicielem. Użyj niestandardowych reporterów lub adnotacji, aby to ujawnić.
Zdefiniuj SLA własności:
- Jeśli test pada, właściciel musi zbadać w ciągu 24 godzin
- Jeśli test jest niestabilny > 5% czasu, właściciel musi naprawić lub wyłączyć go w ciągu 1 tygodnia
- Jeśli test jest wyłączony, właściciel musi naprawić lub usunąć go w ciągu 1 sprintu
Bez SLA własność jest performatywna. Z SLA własność to odpowiedzialność.
2. Tagowanie: uruchamiaj właściwe testy we właściwym czasie
Nie wszystkie testy są równie ważne. Uruchamianie całego pakietu przy każdym commicie jest wolne i marnotrawne. Tagowanie pozwala uruchamiać testy smoke przy każdym commicie, testy regresji nocnie i testy wydajnościowe na żądanie.
Strategia tagowania
// Test smoke: krytyczne ścieżki użytkownika, uruchamia się przy każdym commicie
test('użytkownik może się zalogować', {
annotation: { type: 'smoke' },
}, async ({ page }) => {
// logika testu
});
// Test regresji: pełne pokrycie, uruchamia się nocnie
test('użytkownik może zresetować hasło', {
annotation: { type: 'regression' },
}, async ({ page }) => {
// logika testu
});
// Test wydajnościowy: testy obciążenia/stresu, uruchamia się na żądanie
test('checkout obsługuje 100 równoczesnych użytkowników', {
annotation: { type: 'performance' },
}, async ({ page }) => {
// logika testu
});
Uruchamiaj testy według tagu:
npx playwright test --grep @smoke # Szybki feedback przy każdym commicie
npx playwright test --grep @regression # Pełny pakiet nocnie
npx playwright test --grep @performance # Na żądanie przed wydaniami
Wytyczne testów smoke
Testy smoke powinny być:
- Szybkie — < 10 minut całkowitego czasu wykonania
- Krytyczne — Jeśli te padają, aplikacja jest zepsuta
- Stabilne — < 1% wskaźnik niestabilności
Jeśli test smoke jest niestabilny lub wolny, degraduj go do regresji lub napraw. Niestabilne testy smoke podważają zaufanie.
3. Polityka lokatorów: spójność zmniejsza utrzymanie
Lokatory to najbardziej krucha część testów E2E. Zmiana w strukturze DOM może zepsuć dziesiątki testów. Spójna strategia lokatorów zmniejsza koszt utrzymania.
Zalecana hierarchia lokatorów
1. Atrybuty data-testid (najwyższy priorytet)
await page.click('[data-testid="checkout-button"]');
Plusy: Stabilne, jawny kontrakt testowy z deweloperami. Zmiany w klasach lub tekście nie psują testów.
Minusy: Wymaga współpracy deweloperów, aby dodać atrybuty.
2. Role ARIA i etykiety
await page.click('button[name="checkout"]');
await page.getByRole('button', { name: 'Checkout' }).click();
Plusy: Zachęca do dostępnego markupu. Nie są potrzebne dodatkowe atrybuty.
Minusy: Zmiany w markup dostępności (np. przeformułowanie etykiety) mogą psować testy.
3. Treść tekstowa (używaj oszczędnie)
await page.click('button:has-text("Checkout")');
Plusy: Łatwe do napisania, bez dodatkowego markup.
Minusy: Psuje się, gdy tekst się zmienia (np. internacjonalizacja, testy A/B).
4. Klasy CSS lub ID (ostatnia deska ratunku)
await page.click('.btn-primary');
Plusy: Działa, gdy nic innego nie jest dostępne.
Minusy: Kruche. Zmiany w stylizacji psują testy.
Egzekwuj politykę
Dodaj regułę lintowania do swojego pakietu testów:
// tests/locator-lint.ts
// Pada CI, jeśli testy używają klas CSS bez data-testid
Lub użyj code review: odrzucaj PR-y, które używają kruchych lokatorów, gdy istnieją stabilne alternatywy.
4. Budżet niestabilności: mierz i egzekwuj stabilność
Niestabilne testy to cichy zabójca pakietów testów. Jeden niestabilny test to irytacja. Dziesięć niestabilnych testów oznacza, że nikt nie ufa CI. Ustaw budżet niestabilności i egzekwuj go.
Jak mierzyć niestabilność
Śledź wskaźnik niestabilności na test:
Wskaźnik niestabilności = (liczba uruchomień wymagających ponowienia) / (całkowita liczba uruchomień)
Test, który przechodzi przy pierwszej próbie 95 razy na 100 i wymaga ponowienia 5 razy, ma 5% wskaźnik niestabilności.
Ustawianie budżetu niestabilności
Zalecany budżet:
- Testy smoke: < 1% wskaźnik niestabilności
- Testy regresji: < 5% wskaźnik niestabilności
- Testy eksperymentalne: < 10% wskaźnik niestabilności (lub wyłącz je)
Jeśli test przekracza swój budżet, wyłącz go do czasu naprawy. Niestabilny test jest gorszy niż brak testu, ponieważ marnuje czas i podważa zaufanie.
Śledzenie niestabilności w CI
Użyj wbudowanego śledzenia niestabilności Playwright:
export default defineConfig({
retries: 2,
reporter: [
['list'],
['json', { outputFile: 'test-results.json' }],
],
});
Parsuj test-results.json w CI, aby śledzić:
- Całkowitą liczbę testów
- Wskaźnik przejść
- Wskaźnik niestabilności (testy, które przeszły po ponowieniu)
- Najwolniejsze testy
Opublikuj te metryki na dashboardzie i przeglądaj je co tydzień.
5. Strategia danych testowych: izolacja lub czyszczenie
Testy, które współdzielą dane, są kruche. Jeśli Test A tworzy użytkownika, a Test B usuwa tego użytkownika, i działają równolegle, oba testy padają nieprzewidywalnie.
Opcja 1: Izolacja danych testowych (zalecane)
Każdy test dostaje swoje unikalne dane:
test('użytkownik może się zalogować', async ({ page }) => {
const testUser = `test-${Date.now()}@example.com`;
await createUser(testUser, 'haslo123');
await page.goto('/login');
await page.fill('input[name="email"]', testUser);
await page.fill('input[name="password"]', 'haslo123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});
Plusy: Testy są niezależne, mogą działać w dowolnej kolejności.
Minusy: Dane testowe się gromadzą. Wymaga okresowego czyszczenia.
Opcja 2: Czyszczenie danych testowych
Każdy test czyści po sobie:
test('użytkownik może się zalogować', async ({ page, request }) => {
const testUser = 'test@example.com';
await createUser(testUser, 'haslo123');
await page.goto('/login');
await page.fill('input[name="email"]', testUser);
await page.fill('input[name="password"]', 'haslo123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
// Czyszczenie
await request.delete('/api/users/test@example.com');
});
Plusy: Brak gromadzenia danych.
Minusy: Jeśli czyszczenie zawiedzie (test się zawiesza, timeout CI), dane pozostają i zanieczyszczają przyszłe uruchomienia.
Zalecane podejście
Użyj izolacji dla większości testów. Dodaj zaplanowane zadanie, aby wyczyścić dane testowe starsze niż 24 godziny. To daje ci korzyści izolacji (równoległe wykonywanie, brak współdzielonego stanu) bez nieograniczonego wzrostu danych.
6. Dokumentacja: pakiet testów jako żywy dokument
Twój pakiet testów to dokumentacja. Jeśli deweloper chce wiedzieć “jak działa checkout?” powinien być w stanie przeczytać testy i zrozumieć przepływ.
Pisanie samo-dokumentujących testów
Zły test:
test('test 1', async ({ page }) => {
await page.goto('/');
await page.click('button');
await expect(page.locator('div')).toHaveText('Sukces');
});
Co robi ten test? Co weryfikuje?
Dobry test:
test('użytkownik-gość może ukończyć checkout bez tworzenia konta', async ({ page }) => {
await addItemToCart(page, 'Czerwona koszulka');
await page.goto('/checkout');
await page.fill('input[name="email"]', 'gosc@example.com');
await page.fill('input[name="cardNumber"]', '4242424242424242');
await page.click('button:has-text("Złóż zamówienie")');
await expect(page.locator('[data-testid="order-confirmation"]')).toBeVisible();
await expect(page.locator('[data-testid="order-number"]')).toContainText(/ORDER-\d{6}/);
});
Ten test mówi ci:
- Kto: Użytkownik-gość (nie zalogowany)
- Co: Ukończ checkout
- Jak: Dodaj przedmiot, wypełnij dane, prześlij
- Oczekiwany wynik: Potwierdzenie zamówienia z numerem zamówienia
README dla pakietu testów
Dodaj tests/README.md:
# Pakiet testów E2E
## Uruchamianie testów
- `npm test` — Uruchom wszystkie testy
- `npm run test:smoke` — Uruchom tylko testy smoke
- `npm run test:headed` — Uruchom z widoczną przeglądarką
## Organizacja testów
- `tests/auth/` — Testy autentykacji i autoryzacji (właściciel: auth-team)
- `tests/checkout/` — Testy checkout i płatności (właściciel: payments-team)
- `tests/admin/` — Testy panelu admina (właściciel: platform-team)
## Pisanie nowych testów
- Używaj atrybutów `data-testid` dla lokatorów
- Taguj testy smoke przez `@smoke`
- Czyść dane testowe po każdym teście
## Debugowanie nieudanych testów
1. Pobierz trace z CI
2. Uruchom `npx playwright show-trace trace.zip`
3. Sprawdź oś czasu akcji i żądania sieciowe
To oszczędza godziny czasu onboardingu dla nowych członków zespołu.
7. Progresywny refactoring: poprawiaj z czasem
Pakiety testów nie muszą być perfekcyjne od pierwszego dnia. Muszą poprawiać się z czasem. Przyjmij podejście progresywnego refactoringu:
Miesięczny przegląd zdrowia testów
Co miesiąc przeglądaj:
- Najbardziej niestabilne testy — Napraw lub wyłącz top 5
- Najwolniejsze testy — Zoptymalizuj lub podziel na shardy 10 najwolniejszych
- Wyłączone testy — Ponownie włącz lub usuń testy wyłączone > 1 miesiąc
Zasady refactoringu
- Najpierw spraw, by działało, potem spraw, by było szybkie — Wolny test jest lepszy niż brak testu
- Refaktoryzuj z pewnością — Użyj traces testów, aby sprawdzić, że refaktoryzacje nie zmieniły zachowania
- Małe, przyrostowe zmiany — Refaktoryzuj jeden plik testowy na sprint, a nie cały pakiet na raz
Podsumowanie: to koniec początku
Ta seria obejmowała umiejętności techniczne: pisanie testów, debugowanie awarii, konfigurację CI. Ale utrzymywanie pakietu testów wartościowym przez lata to nie problem techniczny — to problem organizacyjny.
Praktyki w tym poście to różnica między pakietem testów, który gnije, a pakietem testów, który rośnie z twoją firmą:
- Własność — Każdy test ma nazwanego właściciela
- Tagowanie — Uruchamiaj testy smoke przy każdym commicie, testy regresji nocnie
- Polityka lokatorów — Używaj
data-testid, a nie klas CSS - Budżet niestabilności — Mierz i egzekwuj stabilność
- Izolacja danych testowych — Testy nie zakłócają się nawzajem
- Dokumentacja — Testy są czytelne, a onboarding jest płynny
- Progresywny refactoring — Poprawiaj pakiet przyrostowo, a nie wszystko na raz
:::tip[Zacznij od małego, skaluj stopniowo] Nie próbuj wdrożyć wszystkich tych praktyk pierwszego dnia. Zacznij od własności i tagowania. Dodaj politykę lokatorów w następnym sprincie. Wprowadź budżety niestabilności, gdy pakiet jest wystarczająco duży, aby uzasadnić narzut. Zbuduj system, który pasuje do obecnego rozmiaru twojego zespołu, a następnie dostosuj się wraz z rozwojem. :::
Zadanie na ten tydzień: Wybierz jedną praktykę z tego postu i wdróż ją. Jeśli twój pakiet nie ma własności, przypisz właścicieli do 10 najbardziej krytycznych testów. Jeśli nie masz tagowania, otaguj 5 najważniejszych testów jako @smoke i skonfiguruj zadanie CI, aby uruchamiać je przy każdym commicie. Małe zmiany się kumulują.
To kończy serię Podstawy Playwright. Masz teraz narzędzia do pisania szybkich, niezawodnych, łatwych w utrzymaniu testów end-to-end oraz praktyki utrzymywania ich wartościowymi wraz z rozwojem zespołu.
Jeśli jesteś gotowy, aby myśleć bardziej strategicznie o tym, kiedy automatyzować (a nie tylko jak), kontynuuj serię Strategia Automatyzacji zaczynając od Kiedy automatyzować — i kiedy odpuścić.
Koniec serii Podstawy Playwright.