Məzmuna keç

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);
});

requestAPIRequestContext-in bir nümunəsidir. Aşağıdakıları açır:

MetodHTTP 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.


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);
});
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 — 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');
});
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-dur
expect(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.


Ə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();
});

pagerequest-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: pagerequest 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ın
await page.route('**/api/analytics', route => route.abort());
await page.goto('/panel');
// Analytics sorğuları heç vaxt serverə göndərilmədi

nü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 kodu
  • contentType — MIME tipi
  • body — sətir kimi cavab gövdəsi
  • headers — 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.


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.


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ı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ə ya resetDatabase üçün boolean) qaytarır
  • getProducts üçün sorgu sətri qurmaqda URLSearchParams istifadə 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-i apiHelper fixture-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)
  • ApiHelper nümunəsini use(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, error ilə
  • Boş sahələrlə giriş → 400, error ilə
  • POST /api/auth/register → 201, role: 'customer' ilə
  • Dublikat e-poçtla qeydiyyat → 409, error ilə
  • Qısa şifrə (< 6 simvol) → 400, error ilə

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, price olan massiv
  • GET /api/products?category=electronics → bütün nəticələr category: 'electronics'-ə malikdir
  • POST /api/products → 201, yeni id ilə
  • PUT /api/products/:id → zəncir: yarat → yenilə → yeni dəyərləri yoxla
  • DELETE /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 massiv
  • GET /api/users → istifadəçilərdə password xüsusiyyəti yoxdur (təhlükəsizlik yoxlaması)

Hissə B (kombinasiya):

  • apiHelper.createProduct() ilə məhsul yaradın
  • page.goto() ilə /products sə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.