Modul 9: API testi və şəbəkə
Modul 9-a xoş gəldiniz. Burada brauzerdən uzaqlaşıb birbaşa backend-i test edəcəksiniz — sonra hər iki dünyəni birləşdirəcəksiniz. Həmçinin testlər daxilindən şəbəkəni necə ələ keçirəcəyinizi öyrənəcəksiniz.
🎬 Video tezliklə əlavə olunacaq
API testinin nə üçün vacib olduğu
Bölmə: “API testinin nə üçün vacib olduğu”Brauzer UI testləri dəyərlidir, lakin yavaşdır: tək bir giriş + yarat əməliyyatı 3–5 saniyə ala bilər. Eyni əməliyyat HTTP üzərindən 200 ms-dən az çəkir.
API testi sizə üç əsas üstünlük verir:
- Sürət — ekvivalent UI testlərindən 10–50 dəfə sürətlidir
- Dəqiqlik — bütün yığından yox, tək bir endpoint-i ayrıca test edirsiniz
- Məlumat quraşdırma — UI testi başlamadan əvvəl vəziyyəti API vasitəsilə toxum əkin və ya sıfırlayın
Peşəkar test paketlərindəki ümumi nümunə: məlumatı qurmaq üçün API, onun düzgün görüntüləndiyini yoxlamaq üçün brauzer.
Yadda saxla: API testləri UI-dan 10–50 dəfə sürətlidir və tək bir endpoint-i təcrid edir — peşəkar nümunə məlumatı API vasitəsilə qurmaq, sonra onu brauzerdə yoxlamaqdır.
request fixture-u və APIRequestContext
Bölmə: “request fixture-u və APIRequestContext”Playwright daxili HTTP müştəri ilə gəlir. İstənilən testdə request-i destructuring edərək alırsınız:
import { test, expect } from '@playwright/test';
test('GET products 200 qaytarır', async ({ request }) => { const res = await request.get('https://your-app.com/api/products'); expect(res.status()).toBe(200);});request — APIRequestContext-in bir nümunəsidir. Aşağıdakıları açır:
| Metod | HTTP verb |
|---|---|
request.get(url, options?) | GET |
request.post(url, options?) | POST |
request.put(url, options?) | PUT |
request.delete(url, options?) | DELETE |
request.patch(url, options?) | PATCH |
playwright.config.ts-də kontekst üçün baseURL qura bilərsiniz ki, testlərdə tam URL əvəzinə yalnız /api/products yazsanız kifayətdir.
Yadda saxla: { request }-i destructuring edin və .get/.post/.put/.delete/.patch açan daxili APIRequestContext alın; konfiqurasiyada baseURL qurun ki, testlər /api/products kimi qısa yollar işlətsin.
GET və POST sorğuları
Bölmə: “GET və POST sorğuları”GET — məlumatı oxumaq
Bölmə: “GET — məlumatı oxumaq”test('GET /api/products massiv qaytarır', async ({ request }) => { const res = await request.get('/api/products'); expect(res.status()).toBe(200);
const body = await res.json(); expect(Array.isArray(body)).toBe(true); expect(body.length).toBeGreaterThan(0);});POST — məlumat yaratmaq
Bölmə: “POST — məlumat yaratmaq”test('POST /api/products məhsul yaradır', async ({ request }) => { const res = await request.post('/api/products', { data: { name: 'Klaviatura', price: 49.99, category: 'electronics' }, }); expect(res.status()).toBe(201);
const body = await res.json(); expect(body).toHaveProperty('id'); expect(body.name).toBe('Klaviatura');});Sorğu gövdəsini data parametrindən keçirin. Playwright avtomatik olaraq Content-Type: application/json qoyur.
Yadda saxla: request.get() oxuyur; request.post(url, { data }) JSON gövdə göndərir (Content-Type sizin üçün qoyulur). res.status() və ayrıştırılmış await res.json() üzərində yoxlayın.
PUT və DELETE sorğuları
Bölmə: “PUT və DELETE sorğuları”PUT — məlumatı yeniləmək
Bölmə: “PUT — məlumatı yeniləmək”test('PUT /api/products/:id məhsulu yeniləyir', async ({ request }) => { // Əvvəlcə yeniləmək üçün məhsul yaradın const created = await request.post('/api/products', { data: { name: 'Köhnə Ad', price: 10, category: 'alətlər' }, }); const { id } = await created.json();
// İndi yeniləyin const res = await request.put(`/api/products/${id}`, { data: { name: 'Yeni Ad', price: 19.99 }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body.name).toBe('Yeni Ad');});DELETE — məlumatı silmək
Bölmə: “DELETE — məlumatı silmək”test('DELETE /api/products/:id məhsulu silir', async ({ request }) => { const created = await request.post('/api/products', { data: { name: 'Silinəcək', price: 5, category: 'digər' }, }); const { id } = await created.json();
const res = await request.delete(`/api/products/${id}`); expect(res.status()).toBe(200); expect((await res.json()).message).toBe('Product deleted');});Zəncirvari nümunə — yarat → ID-ni saxla → əməliyyat et — etibarlı API test məlumatları idarəetməsinin əsasıdır.
Yadda saxla: yarat → qaytarılan id-ni saxla → onun üzərində əməliyyat et. PUT yeniləyir və yeni gövdəni qaytarır; DELETE silir və təsdiq mesajı qaytarır — yaratmadığınız id-ni heç vaxt sabit kodlamayın.
Status kodları və JSON gövdəsini yoxlamaq
Bölmə: “Status kodları və JSON gövdəsini yoxlamaq”Playwright cavab obyektinə iki yardımçı verir:
// Status kodu yoxlamasıexpect(res.status()).toBe(201);
// ok() — istənilən 2xx üçün true-durexpect(res.ok()).toBeTruthy();
// JSON gövdə yoxlamalarıconst body = await res.json();expect(body).toHaveProperty('id');expect(body.email).toBe('istifadeci@nümunə.com');expect(body).not.toHaveProperty('password'); // təhlükəsizlik yoxlaması!Həmişə həm statusu, həm də gövdə formasını test edin. Sınıq API 200 ilə içəridə xəta mesajı qaytara bilər — yalnız status() bunu tutmaz.
Yadda saxla: həm status (res.status() / res.ok()), həm də gövdə formasını yoxlayın — sınıq endpoint 200 ilə xəta gövdəsi qaytara bilər, ona görə tək status kifayət deyil. not.toHaveProperty('password') ucuz təhlükəsizlik yoxlamasıdır.
Hibrid API + UI testlər
Bölmə: “Hibrid API + UI testlər”Ən güclü nümunə: vəziyyəti qurmaq üçün API, yoxlamaq üçün brauzer.
test('API vasitəsilə yaradılan məhsul UI-da görünür', async ({ page, request }) => { // 1. API ilə məlumat yerləşdirin (sürətli) const res = await request.post('/api/products', { data: { name: 'E2E Vidget', price: 29.99, category: 'vidgetlər' }, }); expect(res.status()).toBe(201);
// 2. Brauzerdə render olunduğunu yoxlayın (əsl test) await page.goto('/products'); await expect(page.getByText('E2E Vidget')).toBeVisible();});page və request-in ikisini də eyni testdə istifadə edə bilərsiniz — hər ikisi eyni test kontekstindən gəlir və kukiləri/auth vəziyyətini paylaşır.
Yadda saxla: page və request eyni konteksti (kuki/auth) paylaşır, ona görə vəziyyəti API ilə sürətlə qurun, sonra brauzerdə yoxlayın — API quraşdırır, UI əsl təkiddir.
page.route ilə şəbəkə müdaxiləsi
Bölmə: “page.route ilə şəbəkə müdaxiləsi”Playwright, serverə çatmadan əvvəl səhifənin etdiyi istənilən şəbəkə sorğusunu ələ keçirə bilər. page.route(nümunə, işləyici) istifadə edin:
// /api/analytics-a olan bütün sorğuları dayandırınawait page.route('**/api/analytics', route => route.abort());
await page.goto('/panel');// Analytics sorğuları heç vaxt serverə göndərilmədinümunə qəbul edir:
- Glob sətirləri:
'**/api/**' - Tam URL-lər:
'https://api.nümunə.com/izlə' - Müntəzəm ifadələr:
/analytics/
Yadda saxla: page.route(nümunə, işləyici) uyğun sorğuları serverə çatmadan əvvəl ələ keçirir; nümunə glob (**/api/**), tam URL və ya regex ola bilər.
Sorğuları bloklamaq (analytics, şəkillər)
Bölmə: “Sorğuları bloklamaq (analytics, şəkillər)”Üçüncü tərəf analytics-i və ya böyük şəkilləri bloklamaq testləri əhəmiyyətli dərəcədə sürətləndirir:
test('səhifə analytics olmadan yüklənir', async ({ page }) => { // Analytics və izləmə skriptlərini blokla await page.route('**/*analytics*', route => route.abort()); await page.route('**/*tracking*', route => route.abort());
await page.goto('/'); await expect(page.getByRole('heading', { name: 'Xoş gəldiniz' })).toBeVisible();});
test('səhifə şəkilsiz yüklənir (daha sürətli)', async ({ page }) => { await page.route('**/*.{png,jpg,jpeg,gif,webp,svg}', route => route.abort()); await page.goto('/products'); // Şəkilləri gözləmədən mətn məzmununu test edin await expect(page.getByText('Klaviatura')).toBeVisible();});Yadda saxla: route.abort() sorğunu atır — yalnız mətn məzmunu önəmli olduqda testləri sürətli və sabit etmək üçün analytics, izləyiciləri və ya şəkilləri bloklayın.
route.fulfill ilə cavabları mock etmək
Bölmə: “route.fulfill ilə cavabları mock etmək”route.fulfill istifadə edərək real şəbəkə cavabını saxta ilə əvəz edin:
test('mock API-dən məhsul siyahısını göstərir', async ({ page }) => { await page.route('**/api/products', route => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify([ { id: 1, name: 'Mock Məhsul', price: 9.99 }, ]), }) );
await page.goto('/products'); await expect(page.getByText('Mock Məhsul')).toBeVisible();});route.fulfill sizə idarə etməyə imkan verir:
status— HTTP status koducontentType— MIME tipibody— sətir kimi cavab gövdəsiheaders— xüsusi cavab başlıqları
Yadda saxla: route.fulfill({ status, contentType, body, headers }) real cavabı saxta ilə əvəz edir — serverin heç vaxt qaytarmaya biləcəyi məlumatla UI-ı sürmək üçün idealdır.
Cavabları dəyişdirmək
Bölmə: “Cavabları dəyişdirmək”Bəzən real sorğunun keçməsinə icazə vermək, lakin brauzer görmədən əvvəl cavabı dəyişdirmək istəyirsiniz. route.fetch() sonra route.fulfill() istifadə edin:
test('API cavabına əlavə sahə qoyur', async ({ page }) => { await page.route('**/api/products', async route => { // Real sorğunun keçməsinə icazə verin const response = await route.fetch(); const body = await response.json();
// Cavabı dəyişdirin const dəyişdirilmiş = body.map((p: any) => ({ ...p, nişan: 'YENİ' }));
await route.fulfill({ response, body: JSON.stringify(dəyişdirilmiş), }); });
await page.goto('/products'); // İndi UI-ın əlavə nişan sahəsini necə idarə etdiyini test edin});Yadda saxla: real cavabı dəyişmək üçün await route.fetch() ilə onu alın, ayrıştırılmış gövdəni redaktə edin, sonra route.fulfill({ response, body }) edin — UI orijinalı yox, sizin dəyişdirilmiş versiyanızı görür.
Xətaları (500) və yavaş şəbəkəni simulyasiya etmək
Bölmə: “Xətaları (500) və yavaş şəbəkəni simulyasiya etmək”Server xətasını simulyasiya etmək
Bölmə: “Server xətasını simulyasiya etmək”test('API 500 qaytardıqda xəta mesajı göstərir', async ({ page }) => { await page.route('**/api/products', route => route.fulfill({ status: 500, contentType: 'application/json', body: JSON.stringify({ error: 'Daxili Server Xətası' }), }) );
await page.goto('/products'); await expect(page.getByText('Bir şey səhv getdi')).toBeVisible();});Bu nümunə xəta sərhədlərini, ehtiyat UI-larını və yenidən cəhd məntiqini test etmək üçün vacibdir — test mühitində real 500-ü etibarlı şəkildə tətikləmək mümkün deyil.
Yavaş şəbəkəni simulyasiya etmək
Bölmə: “Yavaş şəbəkəni simulyasiya etmək”test('yavaş API zamanı yükləmə vəziyyətini göstərir', async ({ page }) => { await page.route('**/api/products', async route => { // Cavabı 2 saniyə gecikdir await new Promise(resolve => setTimeout(resolve, 2000)); await route.continue(); });
await page.goto('/products'); // Yükləmə fırlanıcısının göründüyünü yoxlayın await expect(page.getByTestId('yuklenme-firlanici')).toBeVisible();});Yadda saxla: real tətikləyə bilmədiyiniz xəta UI-larını test etmək üçün 500-ü fulfill edin, yükləmə vəziyyətlərini yoxlamaq üçün route.continue()-dən əvvəl gecikmə await edin — hər ikisi canlı backend-in tələblə etibarlı yaratmadığı şərtlərdir.
page.waitForResponse
Bölmə: “page.waitForResponse”Yoxlamadan əvvəl müəyyən bir cavabın gəlməsini gözləmək lazım olduqda page.waitForResponse istifadə edin:
test('products API-ni gözləyib yoxlayır', async ({ page }) => { // Sorğunu tətikləyən əməliyyatdan ƏVVƏL dinləməyə başlayın const cavabVədi = page.waitForResponse('**/api/products');
await page.goto('/products');
const response = await cavabVədi; expect(response.status()).toBe(200);
// Məlumat yükləndi — UI-ı yoxlamaq təhlükəsizdir await expect(page.getByRole('list')).toBeVisible();});waitForResponse vədini həmişə sorğunu tətikləyən əməliyyatdan əvvəl qurun. Sonra qursanız, cavab artıq gəlib keçmiş ola bilər və sonsuz gözləyəcəksiniz.
Daha mürəkkəb uyğunlaşdırma üçün predikat funksiyasından da istifadə edə bilərsiniz:
const response = await page.waitForResponse( res => res.url().includes('/api/products') && res.status() === 200);Yadda saxla: page.waitForResponse(urlVəYaPredikat)-i sorğunu tətikləyən əməliyyatdan ƏVVƏL qurun, yoxsa onu qaçırıb ilişəcəksiniz; predikat URL və status üzrə birlikdə uyğunlaşdırmağa imkan verir.
Tapşırıqlar
Bölmə: “Tapşırıqlar”Tapşırıq 1 — ApiHelper sinfi yaradın (15 dəq)
Bölmə: “Tapşırıq 1 — ApiHelper sinfi yaradın (15 dəq)”Tətbiqin API endpoint-lərini saran tests/utils/api-helper.ts faylı yaradın.
Tələblər:
- Konstruktor Playwright-in
request-ini (APIRequestContext) qəbul edir və saxlayır - Metodlar:
resetDatabase(),login(email, password),register(name, email, password),getProducts(params?),createProduct(data),updateProduct(id, data),deleteProduct(id),getOrders(),getUsers() - Hər metod
{ status, body }(və yaresetDatabaseüçün boolean) qaytarır getProductsüçün sorgu sətri qurmaqdaURLSearchParamsistifadə edin
Tapşırıq 2 — Fixture qurun (10 dəq)
Bölmə: “Tapşırıq 2 — Fixture qurun (10 dəq)”Aşağıdakı test-data.fixture.ts faylı yaradın:
baseTest-iapiHelperfixture-u ilə genişləndirir- Hər testdən əvvəl avtomatik
api.resetDatabase()çağırır (testin özündə yox, fixture daxilindəki setup funksiyasında) ApiHelpernümunəsiniuse(api)vasitəsilə testə ötürür
Tapşırıq 3 — Auth API testləri (15 dəq)
Bölmə: “Tapşırıq 3 — Auth API testləri (15 dəq)”Bütün giriş və qeydiyyat vəziyyətlərini əhatə edən testlər yazın:
POST /api/auth/login→ 200,{ id, email, role }ilə- Yanlış şifrə ilə giriş → 401,
errorilə - Boş sahələrlə giriş → 400,
errorilə POST /api/auth/register→ 201,role: 'customer'ilə- Dublikat e-poçtla qeydiyyat → 409,
errorilə - Qısa şifrə (< 6 simvol) → 400,
errorilə
Tapşırıq 4 — Məhsulların tam CRUD-u (20 dəq)
Bölmə: “Tapşırıq 4 — Məhsulların tam CRUD-u (20 dəq)”Məhsulun tam həyat dövrü üçün testlər yazın:
GET /api/products→ 200,id,name,priceolan massivGET /api/products?category=electronics→ bütün nəticələrcategory: 'electronics'-ə malikdirPOST /api/products→ 201, yeniidiləPUT /api/products/:id→ zəncir: yarat → yenilə → yeni dəyərləri yoxlaDELETE /api/products/:id→ zəncir: yarat → sil →{ message: 'Product deleted' }yoxla- Mövcud olmayan id ilə
DELETE→ 404
Tapşırıq 5 — Sifarişlər, istifadəçilər və API+UI kombinasiyası (15 dəq)
Bölmə: “Tapşırıq 5 — Sifarişlər, istifadəçilər və API+UI kombinasiyası (15 dəq)”Hissə A:
GET /api/orders→ uzunluğu ≥ 1 olan massivGET /api/users→ istifadəçilərdəpasswordxüsusiyyəti yoxdur (təhlükəsizlik yoxlaması)
Hissə B (kombinasiya):
apiHelper.createProduct()ilə məhsul yaradınpage.goto()ilə/productssəhifəsinə keçin- Məhsulun adının səhifədə göründüyünü yoxlayın
Bonus — Xəta sxemi yardımçısı
Bölmə: “Bonus — Xəta sxemi yardımçısı”Həm status kodunu, həm də body.error-un boş olmayan sətir olduğunu yoxlayan expectError(response, gözlənilənStatus) yardımçısı yazın. Bütün xəta halı testlərini bu yardımçını istifadə edəcək şəkildə yenidən yazın.