
Testowanie to zawód, w którym sztuczna inteligencja nie zastępuje człowieka, tylko zdejmuje z niego najżmudniejszą część roboty. Zamiast rozpisywać dziesiątki przypadków testowych ręcznie, opisujesz funkcję i dostajesz gotowy szkielet do sprawdzenia. Zamiast wymyślać dane testowe, generujesz je jednym poleceniem. W tym artykule przejdziemy przez cały warsztat testera wspieranego przez AI - i co ważne, każdy przykład uruchomiliśmy naprawdę, na jednej małej aplikacji, której kod możesz sobie pobrać.
Pokażemy, jak AI pomaga w testach manualnych, w dokumentacji, w testach API (Postman), we frontendzie (Cypress), w testach wydajnościowych (k6 i Artillery), w bezpieczeństwie (OWASP ZAP) oraz we wpięciu tego wszystkiego w potok CI/CD - mechanizm, który automatycznie uruchamia testy przy każdej zmianie w kodzie. Na koniec zbudujemy małe własne narzędzie i omówimy, jak korzystać z AI bezpiecznie, bez wyciekania firmowych danych.
Z tego artykułu dowiesz się:
- gdzie dokładnie AI przyspiesza pracę testera (mapa obszarów),
- jak wyciągnąć z AI dobre przypadki i dane testowe oraz plan testów,
- jak testować API (Postman + Newman) i frontend (Cypress) z pomocą AI,
- jak zrobić testy wydajnościowe (k6, Artillery) i bezpieczeństwa (ZAP),
- jak wpiąć testy w CI/CD i zbudować własne narzędzie,
- jak bezpiecznie używać AI, nie oddając mu firmowych danych.
Gdzie AI realnie pomaga testerowi
Zacznijmy od mapy. AI nie jest jednym magicznym przyciskiem - wchodzi do pracy testera w kilku konkretnych miejscach. W jednych zaoszczędzi Ci godziny (generowanie przypadków i danych), w innych podpowie składnię narzędzia albo wytłumaczy niezrozumiały wynik. W każdym z tych obszarów zasada jest ta sama: AI proponuje, a Ty weryfikujesz.

Nasze środowisko: mały sklep do testowania
Żeby przykłady były prawdziwe, a nie rysowane, postawiliśmy niewielką aplikację - Sklepik. To zwykły serwer w Node.js: kilka adresów API (lista produktów, szczegóły produktu, logowanie, składanie zamówienia) oraz dwie strony (lista produktów i formularz zamówienia). Wszystkie narzędzia w tym artykule celują właśnie w tę aplikację. Kod możesz pobrać i uruchomić u siebie w dwóch komendach.
git clone https://github.com/konwertyta/sklepik-ai-dla-testerow
cd sklepik-ai-dla-testerow
npm install
npm start # http://localhost:3100Oto fragment obsługi zamówienia. Widać, że jest walidacja stanu magazynowego, ale brakuje sprawdzenia samej ilości:
app.post('/api/orders', (req, res) => {
const { productId, qty } = req.body || {};
const p = products.find(x => x.id === Number(productId));
if (!p) return res.status(404).json({ error: 'not_found' });
// brak walidacji qty > 0 - tutaj chowa się błąd
if (p.stock < qty) return res.status(409).json({ error: 'out_of_stock' });
const order = { id: ++orderSeq, productId: p.id, qty: Number(qty),
total: Math.round(p.price * Number(qty) * 100) / 100 };
res.status(201).json(order);
});Testy manualne: przypadki i dane testowe z AI
Najczęstsze codzienne zadanie testera to zamiana wymagania na listę przypadków testowych. To wdzięczna robota dla AI, bo modele świetnie wypisują warianty pozytywne, negatywne i brzegowe - czyli te na granicy dozwolonych wartości, jak zero czy maksimum. Wklejamy kryteria akceptacji naszej funkcji zamówienia i prosimy o tabelę przypadków. Prompt, którego użyliśmy:
Te trzy rodzaje przypadków testowych warto rozróżniać, bo sprawdzają aplikację z zupełnie różnych stron:
- Pozytywny - poprawne dane i typowe użycie, czyli tak zwana szczęśliwa ścieżka. Sprawdzamy, czy przy dobrych danych aplikacja robi to, co powinna (na przykład przyjmuje zamówienie na 2 sztuki i pokazuje sumę).
- Negatywny - świadomie błędne dane. Sprawdzamy, czy aplikacja poprawnie odmawia (na przykład ujemna ilość albo tekst zamiast liczby - zamówienie ma zostać odrzucone z czytelnym komunikatem).
- Brzegowy - wartości na samej granicy dozwolonego zakresu: zero, jeden, maksimum, dokładnie stan magazynu i stan powiększony o jeden. To najmniej intuicyjny, a zarazem najważniejszy rodzaj, bo właśnie na krawędziach najczęściej chowają się błędy - programista zwykle sprawdza „środek" zakresu, a nie jego brzegi. W naszej aplikacji błąd siedzi dokładnie na brzegu: przy ilości równej zero.
ChatGPT zwrócił komplet przypadków w tabeli. Zwróć uwagę na wiersz Negatywny - ilość 0: model sam zaproponował ten przypadek brzegowy, który za chwilę wykryje realny błąd w naszej aplikacji.

Drugie zadanie to dane testowe. Zamiast wymyślać dziesiątki fikcyjnych klientów, generujemy je z AI. W czacie robimy to raz, na kolanie, ale jeśli chcemy odświeżać dane przy każdym uruchomieniu testów, lepiej zamknąć to w małym skrypcie. Napisaliśmy w Pythonie plik generate_testdata.py, który wysyła jedno zapytanie do API modelu i wypisuje gotowy JSON - ten sam efekt co w czacie, tyle że powtarzalny i bez ręcznego kopiowania. Oto cały skrypt:
import json, re, urllib.request
KEY = open("openai_key.txt").read().strip() # Twój klucz API
PROMPT = ("Wygeneruj 6 realistycznych rekordów klientów do testów sklepu internetowego. "
"Zwróć wyłącznie poprawny JSON - tablicę obiektów z polami: "
"imie, nazwisko, email, telefon, miasto, kod_pocztowy.")
body = {"model": "gpt-4o-mini", "messages": [{"role": "user", "content": PROMPT}]}
req = urllib.request.Request(
"https://api.openai.com/v1/chat/completions",
data=json.dumps(body).encode(),
headers={"Authorization": "Bearer " + KEY, "Content-Type": "application/json"},
)
resp = json.load(urllib.request.urlopen(req))
content = resp["choices"][0]["message"]["content"].strip()
content = re.sub(r"^```(?:json)?\s*|\s*```$", "", content) # zdejmij ramki markdown
print(content)Uruchamiamy go w terminalu poleceniem python generate_testdata.py i dostajemy komplet rekordów w formacie JSON, z poprawnymi polskimi znakami w danych:

Sercem skryptu jest prompt (zmienna PROMPT w kodzie powyżej) - im węższe i bardziej konkretne polecenie, tym mniej model zmyśla:
Dokumentacja: plan i strategia testów w minutę
Plan testów czy strategia to dokumenty, które łatwo odkładać na później. AI robi z nich szkic w kilka sekund - a Ty tylko poprawiasz i dopasowujesz do projektu. Poprosiliśmy o zwięzły plan testów dla Sklepiku:

Testy API: Postman i Newman
Adres API (endpoint - pojedynczy punkt, pod który wysyłamy zapytanie, np. lista produktów) testuje się w Postmanie: wysyłamy zapytanie i sprawdzamy odpowiedź asercjami. Asercja to warunek, który musi być spełniony, na przykład status odpowiedzi równy 200. AI - w Postmanie nazywa się to Postbot - potrafi wygenerować te asercje za nas. Wystarczy pokazać odpowiedź i powiedzieć, co ma sprawdzić:
Efekt to gotowy kod do zakładki Tests. Piszemy go raz, a wykonuje się przy każdym uruchomieniu kolekcji:
// Zakładka "Tests" w Postmanie - asercje, które podpowiada Postbot:
pm.test("status 200", () => pm.response.to.have.status(200));
const body = pm.response.json();
pm.test("zwraca 6 produktów", () => pm.expect(body.count).to.eql(6));
pm.test("items to tablica", () => pm.expect(body.items).to.be.an("array"));
pm.test("pierwszy produkt ma nazwę i cenę", () => {
pm.expect(body.items[0]).to.have.property("name");
pm.expect(body.items[0]).to.have.property("price");
});Kolekcję Postmana da się uruchomić z linii poleceń narzędziem Newman - to ten sam plik kolekcji, tylko odpalany bez klikania. Dzięki temu te same testy API wpniemy później w potok CI. Oto realny wynik: sześć zapytań, dwanaście asercji, wszystkie zielone.

Testy frontendu (E2E): Cypress
Test E2E, czyli od początku do końca, udaje prawdziwego użytkownika: otwiera stronę, klika, wpisuje, sprawdza wynik. Piszemy je w Cypressie. AI dobrze radzi sobie z takim szkieletem - podajemy nazwy pól formularza i opis scenariuszy, a dostajemy gotowy test. Ważne, żeby w prompcie podać konkretne selektory - sposób wskazania elementu na stronie. U nas są to atrybuty data-cy, czyli zwykłe atrybuty HTML dodane w kodzie strony specjalnie na potrzeby testów (np. data-cy="qty" przy polu ilości). Test trzyma się takiego stabilnego znacznika zamiast kruchych klas CSS czy tekstu przycisku, które przy przebudowie wyglądu strony często się zmieniają i psują testy:
Test, który dostaliśmy, ma trzy przypadki: dwie ścieżki, które powinny przejść (zamówienie 2 i 3 sztuk), oraz przypadek brzegowy z ilością 0, który świadomie zakłada, że aplikacja powinna zero odrzucić:
describe('Sklepik - składanie zamówienia', () => {
beforeEach(() => cy.visit('/sklep.html'));
it('ścieżka pozytywna: składa zamówienie na 2 sztuki', () => {
cy.get('[data-cy=product]').select(0);
cy.get('[data-cy=qty]').clear().type('2');
cy.get('[data-cy=order-btn]').click();
cy.get('[data-cy=result]').should('be.visible').and('contain', 'przyjęte');
});
it('inna ilość: zamówienie na 3 sztuki przechodzi', () => {
cy.get('[data-cy=qty]').clear().type('3');
cy.get('[data-cy=product]').select(1);
cy.get('[data-cy=order-btn]').click();
cy.get('[data-cy=result]').should('contain', 'przyjęte');
});
it('przypadek brzegowy: ilość 0 powinna zostać odrzucona', () => {
cy.get('[data-cy=qty]').clear().type('0');
cy.get('[data-cy=order-btn]').click();
cy.get('[data-cy=result]').should('contain', 'Błąd'); // ten test wykryje błąd
});
});Uruchamiamy testy komendą npx cypress run. I tu dzieje się najciekawsze: dwa testy przechodzą, a trzeci - ten brzegowy - czerwony.

Cypress przy każdej awarii sam zapisuje zrzut ekranu z momentu błędu. Widać na nim, co poszło nie tak: formularz z ilością 0 zwrócił zielony komunikat "Zamówienie przyjęte" z sumą 0 zł. To dokładnie ten ukryty błąd z kodu, który pokazaliśmy na początku - i znalazł go przypadek testowy podpowiedziany przez AI, bez zaglądania do źródeł.

Testy wydajnościowe: k6 i Artillery
Test wydajnościowy sprawdza, jak aplikacja znosi obciążenie: wielu użytkowników naraz, przez jakiś czas. Dwa popularne, darmowe narzędzia to k6 (skrypty w JavaScript) i Artillery (scenariusze w YAML - prostym formacie tekstowym, bez pisania kodu). AI pomaga napisać skrypt, dobrać progi i - co równie ważne - zrozumieć wynik.
k6
W k6 opisujemy etapy obciążenia (rozgrzewka, utrzymanie, wygaszanie) i progi - po angielsku thresholds, czyli granice, po przekroczeniu których test uznajemy za nieudany. Nasz skrypt strzela w listę produktów i pilnuje, żeby 95% zapytań zmieściło się poniżej 300 milisekund:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '10s', target: 20 }, // rozgrzewka do 20 wirtualnych użytkowników
{ duration: '20s', target: 20 }, // utrzymanie obciążenia
{ duration: '10s', target: 0 }, // wygaszanie
],
thresholds: {
http_req_duration: ['p(95)<300'], // 95% zapytań poniżej 300 ms
},
};
export default function () {
const res = http.get('http://localhost:3100/api/products');
check(res, {
'status 200': (r) => r.status === 200,
'zwraca 6 produktów': (r) => r.json('count') === 6,
'czas < 300 ms': (r) => r.timings.duration < 300,
});
sleep(1);
}
Tym razem wszystkie progi przeszły - lokalne API odpowiada w kilka milisekund, więc ani czas, ani liczba błędów nie zbliżyły się do granicy. Gdyby jednak któryś próg zaświecił się na czerwono (wolniejszy backend, więcej użytkowników, cięższe zapytania), to właśnie w interpretacji takiego raportu AI pomaga najbardziej: podpowie, czy to realny problem, czy naturalny skok pod obciążeniem.
Artillery
Artillery opisuje to samo bez pisania kodu - w pliku YAML podajemy fazy obciążenia i przepływ: wejdź na listę produktów, potem złóż zamówienie:
config:
target: "http://localhost:3100"
phases:
- duration: 20
arrivalRate: 5
name: "Rozgrzewka"
- duration: 20
arrivalRate: 20
name: "Szczyt obciążenia"
scenarios:
- name: "Przeglądanie i zamówienie"
flow:
- get:
url: "/api/products"
capture:
- json: "$.items[0].id"
as: "pid"
- post:
url: "/api/orders"
json: { productId: "{{ pid }}", qty: 1 }
expect:
- statusCode: 201
Które wybrać? k6 daje wygodne progi w kodzie i świetne percentyle, Artillery kusi prostotą YAML-a. W skrócie:

Bezpieczeństwo: skan OWASP ZAP
Testów bezpieczeństwa nie robi się na oko. OWASP ZAP to darmowe narzędzie, które samo przechodzi po aplikacji i szuka typowych słabości. Najprostszy tryb to skan pasywny - po angielsku baseline, czyli podstawowy przegląd bez atakowania aplikacji. Uruchamiamy go jedną komendą w kontenerze Docker, wskazując adres naszej strony. Wynik to lista ostrzeżeń - u nas osiem, głównie brakujące nagłówki bezpieczeństwa:

ZAP tworzy też czytelny raport HTML z podziałem na poziomy ryzyka. To dobry materiał do przekazania zespołowi deweloperskiemu:

Tu AI pokazuje inną mocną stronę - tłumaczenie. Zamiast googlać każdy skrót, wklejamy listę ostrzeżeń i prosimy o wyjaśnienie oraz priorytety:

CI/CD: testy przy każdej zmianie
Największy zysk z testów automatycznych przychodzi wtedy, gdy uruchamiają się same - przy każdym wgraniu zmiany do repozytorium. To właśnie CI/CD: potok, który buduje aplikację i odpala testy. Napiszemy prosty potok dla GitHub Actions, który instaluje zależności, uruchamia aplikację i odpala nasze testy API przez Newman. Plik zapisujemy w repozytorium pod ścieżką .github/workflows/ci.yml - GitHub sam skanuje katalog .github/workflows/ i każdy plik YAML w środku traktuje jako osobny potok. AI świetnie podpowiada te pliki konfiguracyjne, bo ich składnia jest powtarzalna:
name: Testy Sklepiku
on: [push]
jobs:
testy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm install
- name: Start aplikacji w tle
run: |
node server.js &
npx wait-on http://localhost:3100/api/health
- name: Testy API (Newman)
run: |
npm install -g newman
newman run postman/sklepik_collection.jsonPo wgraniu tego pliku do repozytorium (klasyczne git add, git commit i git push) GitHub sam wykrywa nowy potok i uruchamia go - u nas przy każdym pushu, bo w sekcji on: [push] tak ustawiliśmy. Nie trzeba nic klikać w interfejsie. Poniżej realny przebieg w naszym repozytorium - wszystko na zielono:

Ta sama idea działa na innych platformach. Na GitLabie plik nazywa się .gitlab-ci.yml i leży w głównym katalogu repozytorium (GitLab CI/CD wykrywa go automatycznie):
stages: [test]
testy-api:
stage: test
image: node:20
script:
- npm install
- node server.js &
- npx wait-on http://localhost:3100/api/health
- npm install -g newman
- newman run postman/sklepik_collection.jsonA na Bitbuckecie - bitbucket-pipelines.yml, również w katalogu głównym (Pipelines trzeba raz włączyć w ustawieniach repozytorium):
image: node:20
pipelines:
default:
- step:
name: Testy API
script:
- npm install
- node server.js &
- npx wait-on http://localhost:3100/api/health
- npm install -g newman
- newman run postman/sklepik_collection.jsonWłasne narzędzia: DeadLink checker
Czasem najszybszym testem jest mały skrypt napisany na miarę. Klasyczny przykład to sprawdzarka martwych linków (DeadLink checker): pobiera stronę, wyciąga wszystkie odnośniki i sprawdza, które prowadzą donikąd. Kiedyś pisało się to pół godziny; z AI powstaje w minutę. Prompt:
Dostajemy kompletny, działający skrypt - tu w Pythonie, bez żadnych dodatkowych bibliotek:
import sys, re, urllib.request, urllib.parse
from urllib.error import HTTPError, URLError
start = sys.argv[1]
html = urllib.request.urlopen(start, timeout=10).read().decode("utf-8", "replace")
seen, results = set(), []
for href in re.findall(r'href="([^"#]+)"', html):
url = urllib.parse.urljoin(start, href)
if url in seen:
continue
seen.add(url)
try:
code = urllib.request.urlopen(url, timeout=10).status
status = "OK"
except HTTPError as e:
code, status = e.code, "MARTWY"
except URLError as e:
code, status = "-", f"MARTWY ({e.reason})"
results.append((code, status, url))
print(f"Sprawdzono {len(results)} linków na stronie {start}\n")
print(f"{'KOD':>4} {'STATUS':<26} URL")
print("-" * 76)
for code, status, url in results:
print(f"{str(code):>4} {status:<26} {url}")
dead = [r for r in results if r[1].startswith("MARTWY")]
print(f"\nMartwe linki: {len(dead)} / {len(results)}")Uruchamiamy go na stronie głównej Sklepiku, w której celowo zostawiliśmy kilka martwych linków, i od razu widać wynik:

Ten sam schemat - poproś AI o mały skrypt, uruchom, sprawdź - działa dla dziesiątek drobnych zadań: sprawdzania statusów odpowiedzi, porównywania plików, generowania raportów. To najprostszy sposób, żeby zacząć programować z AI, a od tego już krok do poważniejszej automatyzacji.
Piramida testów a rola AI
Na koniec warto ułożyć to w całość. Klasyczna piramida testów mówi, że najwięcej powinno być szybkich testów niższego poziomu, a najmniej wolnych testów przez interfejs. AI pomaga na każdym piętrze, ale największy zysk daje na dole - tam, gdzie testów jest najwięcej i gdzie generowanie ich ręcznie boli najbardziej.

Bezpieczne użycie AI w testowaniu
Skoro AI ma tyle zalet, gdzie jest haczyk? W dwóch miejscach, o których trzeba pamiętać od pierwszego dnia.
Po pierwsze - dane. Do publicznego czatu nie wkleja się danych produkcyjnych, prawdziwych danych osobowych, wewnętrznych logów ani fragmentów kodu klienta. To kwestia i przepisów (RODO), i umów (NDA). Jeśli musisz pracować na wrażliwych danych, wybierz wersję firmową modelu z gwarancją, że dane nie są używane do uczenia, albo model uruchomiony lokalnie, u siebie. Do testów w zupełności wystarczają dane wymyślone - takie jak te, które sami wygenerowaliśmy.
Po drugie - zaufanie. AI halucynuje: potrafi wymyślić nieistniejący adres API, zły selektor albo asercję, która zawsze przechodzi i niczego nie sprawdza. Dlatego każdy wygenerowany test trzeba uruchomić i zobaczyć, czy naprawdę bada to, co powinien. Zasada z całego artykułu wraca tu ostatni raz: AI generuje, człowiek weryfikuje. W tym układzie tester nie znika - staje się autorem i recenzentem, który robi więcej w krótszym czasie.
Podsumowanie
Przeszliśmy cały warsztat: od przypadków i danych testowych, przez dokumentację, testy API i frontendu, wydajność i bezpieczeństwo, aż po CI/CD i własne narzędzia. W każdym z tych miejsc AI zdjęła z nas część zmudnej roboty, a my zachowaliśmy kontrolę nad tym, co i jak jest testowane. Najlepiej przećwiczyć to na własnej skórze - pobierz kod Sklepiku, odtwórz przykłady i zobacz, ile udaje się zrobić szybciej.
Jeśli chcesz przejść przez to z trenerem-praktykiem, na żywo i na realnych przykładach, przygotowaliśmy szkolenie skrojone dokładnie pod ten zakres.
Komentarze (0)
Brak komentarzy...