Przejdź do głównej zawartości

Wzorce testowania

Przekształć testowanie z obowiązku w supermoce. Naucz się wzorców opartych na AI, które generują kompleksowe zestawy testów, utrzymują je automatycznie i wyłapują błędy przed produkcją.

Tradycyjne wyzwania testowania:

  • Czasochłonne pisanie testów
  • Niekompletne pokrycie przypadków brzegowych
  • Obciążenie utrzymania gdy kod ewoluuje
  • Trudność testowania złożonych scenariuszy

Korzyści testowania wspomaganego przez AI:

  • Generuj testy w sekundach nie godzinach
  • Odkrywaj przypadki brzegowe przegapiane przez ludzi
  • Auto-aktualizuj testy gdy kod się zmienia
  • Twórz złożone scenariusze testowe bez wysiłku

Wpływ: Osiągnij 80%+ pokrycia testami 10x szybciej wyłapując 3x więcej błędów.

Generowanie testów

AI pisze kompleksowe zestawy testów

Odkrywanie przypadków brzegowych

Znajdź scenariusze których nie rozważałeś

Utrzymanie testów

Utrzymuj testy w synchronizacji ze zmianami kodu

Optymalizacja pokrycia

Inteligentnie celuj w nietestowany kod

  1. Przeanalizuj kod

    // Funkcja docelowa
    export async function processPayment(order, paymentMethod) {
    if (!order || !order.total) {
    throw new Error('Nieprawidłowe zamówienie');
    }
    if (order.total <= 0) {
    throw new Error('Kwota zamówienia musi być dodatnia');
    }
    if (!isValidPaymentMethod(paymentMethod)) {
    throw new Error('Nieprawidłowa metoda płatności');
    }
    const result = await paymentGateway.charge({
    amount: order.total,
    currency: order.currency || 'USD',
    method: paymentMethod
    });
    if (result.success) {
    await updateOrderStatus(order.id, 'paid');
    await sendConfirmationEmail(order.customerEmail);
    }
    return result;
    }
  2. Poproś o kompleksowe testy

    @file payment.js
    Wygeneruj kompleksowe testy dla processPayment obejmujące:
    1. Scenariusze szczęśliwych ścieżek
    2. Wszystkie warunki błędów
    3. Przypadki brzegowe
    4. Zachowanie asynchroniczne
    5. Mock zewnętrznych zależności
    6. Testuj awarie bramy płatniczej
    7. Weryfikuj wszystkie efekty uboczne
  3. AI generuje kompletny zestaw testów

    describe('processPayment', () => {
    let mockPaymentGateway;
    let mockUpdateOrderStatus;
    let mockSendConfirmationEmail;
    beforeEach(() => {
    // AI konfiguruje wszystkie niezbędne mocki
    mockPaymentGateway = {
    charge: jest.fn()
    };
    mockUpdateOrderStatus = jest.fn();
    mockSendConfirmationEmail = jest.fn();
    });
    describe('szczęśliwa ścieżka', () => {
    it('powinien przetworzyć prawidłową płatność pomyślnie', async () => {
    const order = {
    id: '123',
    total: 99.99,
    customerEmail: 'test@example.com'
    };
    mockPaymentGateway.charge.mockResolvedValue({
    success: true,
    transactionId: 'tx-123'
    });
    const result = await processPayment(order, 'credit_card');
    expect(mockPaymentGateway.charge).toHaveBeenCalledWith({
    amount: 99.99,
    currency: 'USD',
    method: 'credit_card'
    });
    expect(mockUpdateOrderStatus).toHaveBeenCalledWith('123', 'paid');
    expect(mockSendConfirmationEmail).toHaveBeenCalledWith('test@example.com');
    expect(result.success).toBe(true);
    });
    it('powinien obsłużyć niestandardową walutę', async () => {
    // AI generuje test waluty
    });
    });
    describe('warunki błędów', () => {
    it('powinien rzucić błąd dla brakującego zamówienia', async () => {
    await expect(processPayment(null, 'credit_card'))
    .rejects.toThrow('Nieprawidłowe zamówienie');
    });
    it('powinien rzucić błąd dla zerowej kwoty', async () => {
    const order = { id: '123', total: 0 };
    await expect(processPayment(order, 'credit_card'))
    .rejects.toThrow('Kwota zamówienia musi być dodatnia');
    });
    // AI generuje wszystkie inne scenariusze błędów
    });
    describe('przypadki brzegowe', () => {
    it('powinien obsłużyć timeout bramy płatniczej', async () => {
    // AI tworzy scenariusz timeout
    });
    it('powinien obsłużyć równoczesne płatności', async () => {
    // AI tworzy test warunków wyścigu
    });
    });
    });
// Krok 1: Opisz co chcesz
/**
* @cursor Utwórz funkcję która:
* - Waliduje adresy email
* - Zwraca znormalizowany email (małe litery, przycięty)
* - Obsługuje subadresowanie (user+tag@example.com)
* - Waliduje przeciwko typowym błędom
*
* Napisz testy pierwsze, potem implementację
*/
// AI generuje testy
describe('validateEmail', () => {
it('powinien walidować poprawne emaile', () => {
expect(validateEmail('user@example.com')).toBe('user@example.com');
expect(validateEmail('USER@EXAMPLE.COM')).toBe('user@example.com');
expect(validateEmail(' user@example.com ')).toBe('user@example.com');
});
it('powinien obsłużyć subadresowanie', () => {
expect(validateEmail('user+tag@example.com')).toBe('user@example.com');
expect(validateEmail('user+newsletter@gmail.com')).toBe('user@gmail.com');
});
it('powinien odrzucić nieprawidłowe emaile', () => {
expect(() => validateEmail('invalid')).toThrow();
expect(() => validateEmail('@example.com')).toThrow();
expect(() => validateEmail('user@')).toThrow();
});
it('powinien naprawić typowe błędy', () => {
expect(validateEmail('user@gmial.com')).toBe('user@gmail.com');
expect(validateEmail('user@yahooo.com')).toBe('user@yahoo.com');
});
});
// Krok 1: Przeanalizuj istniejące testy
/**
* @cursor Przeanalizuj te testy i zidentyfikuj:
* 1. Jakie mutacje nadal by przeszły
* 2. Brakujące asercje
* 3. Nietestowane gałęzie
* 4. Słabe przypadki testowe
*
* Następnie wzbogać testy aby wyłapać wszystkie mutacje
*/
// Oryginalny słaby test
it('powinien obliczyć rabat', () => {
const result = calculateDiscount(100, 0.1);
expect(result).toBeTruthy(); // Słaba asercja
});
// AI wzbogaca do silnego testu
it('powinien obliczyć rabat poprawnie', () => {
const result = calculateDiscount(100, 0.1);
expect(result).toBe(90); // Dokładna wartość
expect(result).toBeGreaterThan(0); // Dodatni
expect(result).toBeLessThan(100); // Mniejszy od oryginalnego
// Testuj warunki graniczne
expect(calculateDiscount(100, 0)).toBe(100);
expect(calculateDiscount(100, 1)).toBe(0);
// Testuj nieprawidłowe dane wejściowe
expect(() => calculateDiscount(-100, 0.1)).toThrow();
expect(() => calculateDiscount(100, 1.5)).toThrow();
});
// Generuj testy regresji wizualnej
/**
* @cursor Utwórz testy regresji wizualnej dla wszystkich komponentów:
* 1. Wygeneruj test dla każdego stanu komponentu
* 2. Dołącz punkty przerwania responsywne
* 3. Testuj motywy ciemny/jasny
* 4. Obsłuż stany ładowania/błędów
* 5. Przechwyć stany interakcji (hover, focus)
*/
// AI generuje kompleksowe testy wizualne
describe('Regresja wizualna przycisku', () => {
const sizes = ['small', 'medium', 'large'];
const variants = ['primary', 'secondary', 'danger'];
const themes = ['light', 'dark'];
const states = ['default', 'hover', 'focus', 'disabled'];
sizes.forEach(size => {
variants.forEach(variant => {
themes.forEach(theme => {
states.forEach(state => {
it(`powinien pasować ${size} ${variant} ${state} w motywie ${theme}`, async () => {
const component = render(
<ThemeProvider theme={theme}>
<Button
size={size}
variant={variant}
disabled={state === 'disabled'}
>
Kliknij mnie
</Button>
</ThemeProvider>
);
if (state === 'hover') {
await userEvent.hover(component.getByRole('button'));
} else if (state === 'focus') {
await userEvent.tab();
}
await expect(component).toMatchScreenshot(
`button-${size}-${variant}-${state}-${theme}.png`
);
});
});
});
});
});
});
// Wygeneruj zestaw testów wydajności
/**
* @cursor Utwórz testy wydajności dla naszego API:
* 1. Testowanie obciążenia (normalny ruch)
* 2. Testowanie stresu (szczytowy ruch)
* 3. Testowanie skoków (nagły ruch)
* 4. Testowanie wytrzymałości (utrzymane obciążenie)
* 5. Mierz czasy odpowiedzi, wskaźniki błędów, przepustowość
*/
// AI generuje testy wydajności k6
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';
const errorRate = new Rate('errors');
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Rozbieg
{ duration: '5m', target: 100 }, // Pozostań na 100 użytkowników
{ duration: '2m', target: 200 }, // Rozbieg do 200
{ duration: '5m', target: 200 }, // Pozostań na 200
{ duration: '2m', target: 0 }, // Rozbiegnij w dół
],
thresholds: {
http_req_duration: ['p(95) < 500'], // 95% poniżej 500ms
errors: ['rate < 0.1'], // Wskaźnik błędów poniżej 10%
},
};
export default function () {
// Testuj przepływ logowania użytkownika
const loginRes = http.post('https://api.example.com/login', {
email: 'test@example.com',
password: 'password123'
});
check(loginRes, {
'logowanie udane': (r) => r.status === 200,
'czas odpowiedzi OK': (r) => r.timings.duration < 500,
});
errorRate.add(loginRes.status !== 200);
sleep(1); // Czas myślenia
// Testuj endpointy API z auth
const token = loginRes.json('token');
const headers = { Authorization: `Bearer ${token}` };
// AI generuje testy dla wszystkich endpointów...
}
// AI generuje testy oparte na właściwościach
/**
* @cursor Utwórz testy oparte na właściwościach używając fast-check:
* Testuj właściwości matematyczne które zawsze powinny być prawdziwe
*/
import fc from 'fast-check';
describe('Narzędzia tablicowe', () => {
it('powinien utrzymać długość przy tasowaniu', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const shuffled = shuffle([...arr]);
return shuffled.length === arr.length;
})
);
});
it('powinien zawierać te same elementy po tasowaniu', () => {
fc.assert(
fc.property(fc.array(fc.integer()), (arr) => {
const shuffled = shuffle([...arr]);
const sortedOriginal = [...arr].sort();
const sortedShuffled = [...shuffled].sort();
return JSON.stringify(sortedOriginal) === JSON.stringify(sortedShuffled);
})
);
});
});
// Testy kontraktów opartych na konsumentach
/**
* @cursor Wygeneruj testy kontraktów między:
* - Frontend (konsument)
* - Backend API (dostawca)
*
* Dołącz wszystkie endpointy i formaty danych
*/
// AI generuje testy Pact
describe('Kontrakt API użytkownika', () => {
const provider = new Pact({
consumer: 'Frontend',
provider: 'UserAPI',
});
describe('pobierz użytkownika', () => {
it('powinien zwrócić szczegóły użytkownika', async () => {
// Oczekiwanie konsumenta
await provider.addInteraction({
state: 'użytkownik istnieje',
uponReceiving: 'żądanie użytkownika',
withRequest: {
method: 'GET',
path: '/users/123',
headers: { Accept: 'application/json' },
},
willRespondWith: {
status: 200,
headers: { 'Content-Type': 'application/json' },
body: {
id: '123',
name: like('Jan Kowalski'),
email: term({
matcher: '.+@.+\\..+',
generate: 'jan@example.com'
}),
createdAt: iso8601DateTime(),
},
},
});
// Testuj kod konsumenta
const user = await fetchUser('123');
expect(user.id).toBe('123');
expect(user.name).toBeTruthy();
expect(user.email).toMatch(/@/);
});
});
});
// Auto-aktualizuj testy gdy kod się zmienia
/**
* @cursor Funkcja processOrder zmieniła się.
* Zaktualizuj wszystkie powiązane testy aby pasowały do nowej sygnatury
* i zachowania zachowując pokrycie.
*
* Stare: processOrder(orderId, userId)
* Nowe: processOrder(order, options)
*/
// AI automatycznie aktualizuje wszystkie dotknięte testy
MetrykaTestowanie manualneWspomagane przez AIPoprawa
Czas tworzenia testów4 godziny20 minut92% szybciej
Pokrycie kodu45%85%89% wzrost
Znalezione przypadki brzegowe5-1030-505x więcej
Czas utrzymania2 godz/tydzień15 min/tydzień87% mniej
Wskaźnik ucieczki błędów12%3%75% redukcja
  • Jasne zrozumienie wymagań
  • Środowisko testowe przygotowane
  • Zależności zmockowane/stubowane
  • Dane testowe dostępne
  • Pipeline CI/CD gotowy
  • Testy są niezależne
  • Jasne opisy testów
  • Odpowiednie setup/teardown
  • Brak zakodowanych wartości
  • Szybki czas wykonania
  • Szczęśliwe ścieżki przetestowane
  • Warunki błędów pokryte
  • Przypadki brzegowe dołączone
  • Wydajność zwalidowana
  • Scenariusze bezpieczeństwa przetestowane

Opanuj wzorce testowania aby:

  • Osiągnąć 80%+ pokrycia bez wysiłku
  • Wyłapać błędy przed produkcją
  • Zmniejszyć czas cyklu QA
  • Umożliwić pewną refaktoryzację

Kontynuuj z:

Pamiętaj: Najlepszy zestaw testów to ten który wyłapuje błędy, działa szybko i sam się utrzymuje. Pozwól AI obsłużyć powtarzalne części podczas gdy ty skupiasz się na strategii testów i jakości.