Azimjon Abdurasulov

QA 11: API testing (2) — Test dizayn, negative testlar, JSON Schema, Auth, Mock va Contract testing

🧪 QA Engineer — Zero to Hero

QA 11: API testing (2) — Test dizayn, negative testlar, JSON Schema, Auth, Mock va Contract testing

Holat: 2026-yil oktabr. Bu darsda API'ni "chuqur" testlaymiz: javobni 7 qatlamda tekshirish, maydonlar bo'yicha negative test dizayn, JSON Schema, autentifikatsiya va avtorizatsiya matritsasi, mock va contract testing, GraphQL/WebSocket/gRPC xususiyatlari. Darsdagi barcha "real topilmalar" restful-booker, Toolshop API va ochiq GraphQL API'da curl/Newman 6.2.3/Ajv 8.20 bilan haqiqatan ishga tushirib olingan natijalar.

Bu darsdan keyin siz: API uchun "faqat 200 qaytdimi?" darajasidan chiqib, ma'lumot buzilishi, xavfsizlik va shartnoma (contract) bug'larini topadigan test to'plamini loyihalay olasiz.

  • javobni status'dan tortib yon ta'sirlargacha 7 qatlamda tekshirasiz;
  • har bir maydon uchun negative test'larni tizimli chiqarasiz;
  • JSON Schema yozasiz va uni Postman hamda Node.js'da ishlatasiz;
  • 401/403, token muddati, rollar va IDOR bo'yicha test matritsasi tuzasiz;
  • mock, contract testing va GraphQL'ning "200 ichidagi xato"si kabi tuzoqlarni bilasiz.

Mundarija

  1. API javobini 7 qatlamda tekshirish
  2. API test dizayni: CRUD matritsasi
  3. Maydonlar bo'yicha negative test dizayn
  4. Real topilmalar: restful-booker
  5. Real topilmalar: Toolshop API
  6. Xato javoblari formati va izchilligi
  7. Idempotentlik, parallel so'rovlar, sahifalash va rate limiting
  8. JSON Schema bilan sxemani tekshirish
  9. Autentifikatsiya va avtorizatsiya testlari
  10. Mock'lash: back-end tayyor bo'lmaganda
  11. Contract testing
  12. GraphQL, WebSocket, SSE va gRPC — QA uchun farqlar
  13. API test checklisti
  14. Amaliy mashqlar
  15. Xulosa
  16. Manbalar

1. API javobini 7 qatlamda tekshirish {#layers}

Junior tester ko'pincha bitta narsani tekshiradi: "status 200 mi?". Lekin 200 ichida noto'g'ri narx, null bo'lib qolgan maydon yoki boshqa foydalanuvchining ma'lumoti bo'lishi mumkin. API javobini qatlam-qatlam tekshiring:

API javobini 7 qatlamda tekshirish va har qatlam uchun restful-booker'da topilgan real holatlar

#QatlamSavolPostman'da qanday
1Status kodKod holatga mosmi (201 yaratishda, 422 validatsiyada)?pm.response.to.have.status(201)
2Header'larContent-Type, Location, Allow, kesh va xavfsizlik header'lari to'g'rimi?pm.response.headers.get(...)
3Sxema va tiplarMajburiy maydonlar bormi, tiplar to'g'rimi, ortiqcha maydon yo'qmi?pm.response.to.have.jsonSchema(schema)
4QiymatlarYuborilgan qiymat aynan saqlandimi? Hisob-kitob to'g'rimi?pm.expect(body.total).to.eql(479000)
5Biznes qoidalarDomen qoidalari bajarildimi (bepul yetkazish ≥ 300 000 so'm)?Qoida bo'yicha assertion
6Yon ta'sirlarDB'ga yozildimi, ro'yxatda ko'rindimi, email/push ketdimi?Qo'shimcha GET, SQL (QA 12)
7NofunksionalXatolar to'g'ri ushlanadimi, vaqt, xavfsizlik, limitlarresponseTime, negative va auth testlar

🧠 Esda tuting: 1–2 qatlam — "server javob berdimi", 3–5 — "to'g'ri javob berdimi", 6–7 — "tizim to'g'ri ishladimi". Yaxshi QA uchala savolga ham javob qidiradi.


2. API test dizayni: CRUD matritsasi {#crud-matrix}

Har bir resurs uchun CRUD matritsasi tuzing: qatorlarda amallar, ustunlarda tekshiruv turlari. DemoBozor'ning xayoliy cart/items resursi uchun:

AmalPositiveNegative (ma'lumot)ChegaraviyAuth/rolHolat/yon ta'sir
POST /cart/itemsMavjud mahsulot, quantity: 1productId yo'q, string, mavjud bo'lmaganquantity 0, 1, 99, 100Token'siz → 401Savatcha summasi va yetkazish narxi qayta hisoblandi
GET /cartBo'sh va to'la savatcha—50 xil mahsulot (limit)Boshqa foydalanuvchi savatchasi ko'rinmaydiNarxlar joriy (keshdan eski emas)
PATCH /cart/items/{id}quantity: 3quantity: "3", null, -11 → 99, 99 → 100Begona id → 403/404Summa yangilandi
DELETE /cart/items/{id}Mavjud elementMavjud bo'lmagan idOxirgi elementni o'chirishBegona idIkkinchi DELETE nima qaytaradi (idempotentlik)

Bu jadval keyin test case'larga aylanadi (QA 7) va avtomatlashtiriladi (QA 20).


3. Maydonlar bo'yicha negative test dizayn {#field-design}

Har bir maydonga QA 5 dagi EP/BVA texnikalarini qo'llang va quyidagi "universal" mutatsiyalarni sinab ko'ring:

KategoriyaMutatsiyalar
MavjudlikMajburiy maydon yo'q; null; bo'sh qator ""; faqat probellar " "
TipSon o'rniga string ("450", "abc"), boolean o'rniga "yes", obyekt o'rniga massiv
Raqamlar0, manfiy, kasr (450.75), juda katta (9007199254740993), ilmiy yozuv (1e3)
Uzunlikmin−1, min, max, max+1; 10 000+ belgi
Matn tarkibiUnicode (Gʻayrat, Ўрол), emoji, RTL matn, boshida/oxirida probel, yangi qator \n
Xavfsizlik zondlari' OR '1'='1, <script>alert(1)</script> — kutilgani: xavfsiz ishlov (rad etish yoki to'g'ri escape), hech qachon 500 yoki bajarilish emas (QA 24)
SanaNoto'g'ri format, mavjud bo'lmagan sana (2026-02-30), kabisa kuni (2028-02-29), o'tmish/kelajak, teskari oraliq
EnumRo'yxatdan tashqari qiymat, kichik harf (paid vs PAID)
MassivBo'sh [], bitta element, limitdan ortiq, dublikatlar
Ortiqcha maydonlarNoma'lum maydon; "role": "admin", "isAdmin": true kabi "mass assignment" urinishi
FormatBuzilgan JSON; Content-Type yo'q yoki noto'g'ri; bo'sh body

DemoBozor POST /api/v1/cart/items uchun namuna:

#BodyKutilgan
1{"productId": 1042, "quantity": 1}201
2{"productId": 1042, "quantity": 99}201 (yuqori chegara)
3{"productId": 1042, "quantity": 100}422, xato quantity maydoniga bog'langan
4{"productId": 1042, "quantity": 0}422
5{"productId": 1042, "quantity": "2"}422 (yoki hujjatda yozilgan bo'lsa — aniq konvertatsiya), lekin jimgina emas
6{"productId": 1042, "quantity": 2.5}422 — kesib 2 qilish emas
7{"quantity": 1}422, "productId majburiy"
8{"productId": 999999999, "quantity": 1}404 yoki 422 (mahsulot yo'q)
9{"productId": 1042, "quantity": 1, "price": 1}201, lekin narx serverdagi narx bo'lishi shart (client narxi e'tiborga olinmaydi)
10Buzilgan JSON400
11To'g'ri JSON, Content-Type: text/plain415
12Token'siz401

💡 Maslahat: 9-qator — eng qimmat bug'lardan biri. Agar server client yuborgan narxga ishonsa, foydalanuvchi choynakni 1 so'mga sotib oladi. Narx, chegirma, rol kabi "server hal qiladigan" qiymatlarni har doim so'rovga qo'shib ko'ring.


4. Real topilmalar: restful-booker {#findings-rb}

Endi nazariyani haqiqiy API'da sinaymiz. Quyidagi so'rovlarning hammasi POST https://restful-booker.herokuapp.com/booking ga Content-Type: application/json bilan yuborilgan (2026-yil 9-oktabr):

#YuborilganJavobSaqlangan qiymatBaho
1firstname yo'q500 Internal Server Error—Bug: validatsiya o'rniga server xatosi
2Bo'sh obyekt {}500—Bug
3"totalprice": "abc"200"totalprice": nullBug: jim ma'lumot yo'qotilishi
4"totalprice": -100200-100Bug: manfiy narx
5checkout < checkin200teskari sanalarBug: biznes qoida yo'q
6"checkin": "2026-13-45"200"0NaN-aN-aN"Bug: yaroqsiz sana "axlat"ga aylandi
7"firstname": ""200""Bug: bo'sh ism
8Buzilgan JSON400 Bad Request—✅ To'g'ri
9To'g'ri JSON, Content-Type: text/plain500—Bug: 415 kutiladi
10"Gʻayrat 😀", "Oʻrinboyev" + "isAdmin": true200Unicode to'g'ri; isAdmin e'tiborsiz qoldirildi✅ To'g'ri
11"depositpaid": "yes"200trueJim konvertatsiya (hujjatlanmagan)
12"totalprice": 450.75200450Bug: summa jimgina kesildi
13firstname — 100 000 ta A200100 000 belgiBug: uzunlik cheklovi yo'q
14GET /booking/abc404—✅ Qabul qilinadi
15GET /booking?checkin=2026-13-99500—Bug: filtr parametri validatsiyasiz

Qiziq tafsilot: PUT da firstname yo'q bo'lsa server 400 qaytaradi, POST da esa xuddi shu holat 500. Ya'ni bir API ichida validatsiya izchil emas — bu ham alohida topilma.

Takrorlash uchun buyruqlar:

BASE=https://restful-booker.herokuapp.com

# 3-holat: narx string bo'lsa
curl -s -X POST $BASE/booking -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d '{"firstname":"Aziz","lastname":"Karimov","totalprice":"abc","depositpaid":true,
       "bookingdates":{"checkin":"2026-11-01","checkout":"2026-11-05"}}'
# {"bookingid":529,"booking":{...,"totalprice":null,...}}

# 9-holat: noto'g'ri Content-Type
curl -s -o /dev/null -w "%{http_code}\n" -X POST $BASE/booking -H 'Content-Type: text/plain' \
  -d '{"firstname":"Aziz","lastname":"Karimov","totalprice":450,"depositpaid":true,
       "bookingdates":{"checkin":"2026-11-01","checkout":"2026-11-05"}}'
# 500

Topilmani bug report'ga aylantirish

Sarlavha: [API] POST /booking: totalprice'ga string yuborilsa 200 qaytadi va narx null bo'lib saqlanadi

Muhit: https://restful-booker.herokuapp.com (public), 2026-10-09
Qadamlar:
  1. POST /booking, Content-Type: application/json
  2. Body: {"firstname":"Aziz","lastname":"Karimov","totalprice":"abc","depositpaid":true,
           "bookingdates":{"checkin":"2026-11-01","checkout":"2026-11-05"}}
Kutilgan: 400/422, xato totalprice maydoniga bog'langan; booking yaratilmaydi
Haqiqiy:  200, bookingid qaytadi, saqlangan booking'da "totalprice": null
Jiddiylik: Major — ma'lumot jimgina buziladi, keyin hisob-kitoblar noto'g'ri bo'ladi
Qo'shimcha: o'xshash jim konvertatsiyalar — 450.75 → 450, "yes" → true (alohida ticket'lar)
Ilova: curl buyrug'i (yuqorida)

Bug report yozish san'ati batafsil: QA 8.


5. Real topilmalar: Toolshop API {#findings-toolshop}

Toolshop (https://practicesoftwaretesting.com) — testlashni mashq qilish uchun yaratilgan internet-do'kon; API'si https://api.practicesoftwaretesting.com. Demo xaridor: customer@practicesoftwaretesting.com / welcome01. 2026-yil 9-oktabrdagi kuzatuvlar:

So'rovJavobBaho
GET /products?page=1200, {"current_page":1,"per_page":9,"last_page":6,"total":50,"data":[...]}✅
GET /products?page=999200, data: [], from: null✅ Mantiqiy
GET /products?page=-1 va page=abc200, jimgina 1-sahifaMunozarali: 400 yoki hujjatlangan xatti-harakat kerak
GET /products?sort=price,ascEng arzondan boshlab✅
GET /products?between=price,1,10total: 9✅
GET /products?sort=hacker,up500 {"message":"Something went wrong"}Bug: noto'g'ri parametr serverni yiqitadi
GET /products/NOTEXIST404 {"message":"Requested item not found"}✅
GET /products/search?q=<script>alert(1)</script>200, bo'sh natija✅ Xavfsiz ishlov
POST /users/register faqat first_name bilan422 + maydonlar bo'yicha xatolar✅ Namunali validatsiya
POST /users/login noto'g'ri parol401 {"error":"Unauthorized"}✅ kod, lekin pastga qarang
GET /users/me token'siz401 {"message":"Unauthorized"}✅ kod
Xaridor token'i bilan GET /users (admin)403 {"message":"Forbidden"}✅ To'g'ri avtorizatsiya
GET /users/logout, so'ng shu token bilan GET /users/me200, keyin 401✅ Logout token'ni bekor qiladi

Namunali 422 javobi — maydonlar bo'yicha aniq xatolar:

{
  "last_name": ["The last name field is required."],
  "email": ["The email field is required."],
  "password": ["The password field is required."]
}

Ikki nozik topilma:

  1. Xato formati izchil emas: login xatosida kalit error, boshqa endpoint'larda message. Client dasturchisi har endpoint uchun alohida kod yozishga majbur.
  2. 401 javoblarida WWW-Authenticate header'i yo'q — HTTP standarti (RFC 9110) 401 uchun bu header'ni talab qiladi. Kichik, lekin spec bo'yicha topilma.

6. Xato javoblari formati va izchilligi {#error-format}

Xato javobi foydalanuvchi va dasturchi uchun "yo'l ko'rsatkich". Yaxshi xato javobi:

  • to'g'ri status kod;
  • mashina o'qiy oladigan kod ("code": "QUANTITY_OUT_OF_RANGE") va inson o'qiy oladigan xabar;
  • 422 da qaysi maydon va nima xato;
  • X-Request-ID kabi kuzatish identifikatori;
  • ichki tafsilotlar yo'q: stack trace, SQL so'rov, fayl yo'llari, kutubxona versiyalari.

Standart format — RFC 9457 "Problem Details" (Content-Type: application/problem+json). DemoBozor uchun namuna (xayoliy):

{
  "type": "https://api.demobozor.example/errors/quantity-out-of-range",
  "title": "Miqdor ruxsat etilgan oraliqda emas",
  "status": 422,
  "detail": "quantity 1 dan 99 gacha bo'lishi kerak, yuborilgan: 120",
  "instance": "/api/v1/cart/items",
  "errors": [{ "field": "quantity", "code": "QUANTITY_OUT_OF_RANGE" }]
}

QA tekshiruvlari:

  • barcha endpoint'larda xato tuzilishi bir xilmi (Toolshop'dagi error vs message holati);
  • Accept-Language: uz yuborilganda xabar o'zbekchami (agar talab bo'lsa);
  • 500 javobida ichki ma'lumot sizib chiqmayaptimi (QA 10 dagi Petstore NumberFormatException misoli);
  • mobil ilova va web xato kodini to'g'ri tarjima qilib, foydalanuvchiga tushunarli xabar ko'rsatyaptimi.

7. Idempotentlik, parallel so'rovlar, sahifalash va rate limiting {#idempotency}

Dublikat va idempotentlik

Bir xil POST /booking ni ketma-ket ikki marta yuborganimizda restful-booker ikkita booking yaratdi (ikki xil bookingid). DemoBozor'da bu "bitta tugma — ikkita buyurtma" degani. Testlar:

  • "Buyurtma berish"ni ikki marta tez bosish (UI) va bir xil so'rovni ikki marta yuborish (API);
  • tarmoq uzilib, ilova so'rovni qayta yuborganda (Proxyman Repeat bilan simulyatsiya — QA 15);
  • agar API Idempotency-Key kabi mexanizmni qo'llasa — bir xil kalit bilan ikkinchi so'rov yangi buyurtma yaratmasligi.

Parallel so'rovlar (race condition)

Ikki so'rov bir vaqtda kelsa, natija tasodifiy bo'lib qolishi mumkin. Bash'da & va wait bilan oddiy tajriba:

#!/usr/bin/env bash
# Bir booking'ga ikki "foydalanuvchi" bir vaqtda PATCH yuboradi
BASE=https://restful-booker.herokuapp.com
AUTH='Authorization: Basic YWRtaW46cGFzc3dvcmQxMjM='

ID=$(curl -s -X POST $BASE/booking -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d '{"firstname":"Aziz","lastname":"Karimov","totalprice":450,"depositpaid":true,
       "bookingdates":{"checkin":"2026-11-01","checkout":"2026-11-05"}}' \
  | python3 -c 'import sys, json; print(json.load(sys.stdin)["bookingid"])')

for price in 500 700; do
  curl -s -o /dev/null -w "PATCH totalprice=$price → %{http_code}\n" -X PATCH $BASE/booking/$ID \
    -H 'Content-Type: application/json' -H 'Accept: application/json' -H "$AUTH" \
    -d "{\"totalprice\": $price}" &
done
wait

curl -s $BASE/booking/$ID -H 'Accept: application/json' \
  | python3 -c 'import sys, json; print("Yakuniy narx:", json.load(sys.stdin)["totalprice"])'

Uch marta ishga tushirganimizda yakuniy narx 700, 700, 500 chiqdi — "oxirgi yozgan yutadi" va kim oxirgi bo'lishi tasodifiy. DemoBozor'da xuddi shu turdagi muhim testlar: bitta martalik promo-kodni ikki parallel buyurtmada ishlatish, omborda 1 dona qolgan mahsulotni ikki kishi bir vaqtda sotib olishi, ikki tabdan bir vaqtda to'lov.

⚠️ Diqqat: Parallel va ko'p so'rovli testlarni faqat o'z test muhitingizda yoki ruxsat berilgan amaliyot API'larida, kichik sonlar bilan qiling. Begona serverga yuzlab so'rov yuborish — hujum sifatida qabul qilinishi mumkin. Yuklama testlari: QA 23.

Sahifalash, saralash va filtr

Toolshop'da total: 50, per_page: 9 → last_page: 6. Kutilgan natijalar: 5-sahifa — 37–45, 6-sahifa — 46–50 (5 ta), 7-sahifa — bo'sh. Haqiqatan ham shunday chiqdi. Sahifalash uchun test g'oyalari:

  • birinchi, oxirgi va oxirgidan keyingi sahifa;
  • total va barcha sahifalardagi elementlar yig'indisi teng;
  • sahifalar orasida dublikat yoki tushib qolgan element yo'q (ayniqsa saralash barqaror bo'lmasa yoki ro'yxat test paytida o'zgarsa);
  • page=0, manfiy, harf; per_page limitidan katta qiymat;
  • saralash yo'nalishlari va teng qiymatlar (bir xil narxli mahsulotlar tartibi);
  • filtrlar kombinatsiyasi va natija bo'sh bo'lganda javob formati.

Rate limiting

Server bir mijozdan keladigan so'rovlar sonini cheklashi mumkin: limitdan oshganda 429 Too Many Requests, ko'pincha Retry-After header'i bilan. Ba'zi API'lar limitni header'larda ko'rsatadi — masalan, jsonplaceholder javobida x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset bor. DemoBozor qoidasi bo'yicha test: 3 marta noto'g'ri SMS kod → 4-urinish bloklanadi, blok 15 daqiqa davom etadi, blok tugagach yana ishlaydi, boshqa telefon raqamiga ta'sir qilmaydi.


8. JSON Schema bilan sxemani tekshirish {#json-schema}

JSON Schema — JSON ma'lumotining tuzilishini tasvirlaydigan standart (joriy versiya — draft 2020-12). U "qanday maydonlar bo'lishi, qanday tipda, qanday chegaralarda" degan qoidalarni bir joyda yozish imkonini beradi.

Kalit so'zMa'nosiMisol
typeTip"string", "integer", ["string", "null"]
propertiesObyekt maydonlari{"firstname": {...}}
requiredMajburiy maydonlar["firstname", "lastname"]
additionalPropertiesOrtiqcha maydonga ruxsatfalse — kutilmagan maydon = xato
enumRuxsat etilgan qiymatlar["CREATED", "PAID"]
minimum, exclusiveMinimum, maximumSon chegaralari{"minimum": 0}
minLength, maxLength, patternMatn cheklovlari{"pattern": "^\\+998\\d{9}$"}
formatSemantik format"date", "email", "uuid"
items, minItemsMassiv elementlari{"items": {"$ref": "#/$defs/product"}}

Node.js'da Ajv bilan (draft 2020-12)

Ataylab "yomon" booking yaratamiz va uni qat'iy sxema bilan tekshiramiz:

// validate-booking.mjs — restful-booker javobini JSON Schema (draft 2020-12) bilan tekshirish
// O'rnatish: npm i ajv ajv-formats
import Ajv2020 from "ajv/dist/2020.js";
import addFormats from "ajv-formats";

const bookingSchema = {
  $schema: "https://json-schema.org/draft/2020-12/schema",
  type: "object",
  required: [
    "firstname",
    "lastname",
    "totalprice",
    "depositpaid",
    "bookingdates",
  ],
  properties: {
    firstname: { type: "string", minLength: 1, maxLength: 50 },
    lastname: { type: "string", minLength: 1, maxLength: 50 },
    totalprice: { type: "integer", minimum: 0 },
    depositpaid: { type: "boolean" },
    bookingdates: {
      type: "object",
      required: ["checkin", "checkout"],
      properties: {
        checkin: { type: "string", format: "date" },
        checkout: { type: "string", format: "date" },
      },
    },
    additionalneeds: { type: "string" },
  },
  additionalProperties: false,
};

const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);
const validate = ajv.compile(bookingSchema);

const BASE = "https://restful-booker.herokuapp.com";

// 1) Ataylab "yomon" booking yaratamiz: bo'sh ism, manfiy narx, noto'g'ri sana
const created = await fetch(`${BASE}/booking`, {
  method: "POST",
  headers: { "Content-Type": "application/json", Accept: "application/json" },
  body: JSON.stringify({
    firstname: "",
    lastname: "Karimov",
    totalprice: -100,
    depositpaid: true,
    bookingdates: { checkin: "2026-13-45", checkout: "abc" },
  }),
}).then((r) => r.json());

// 2) Serverda qanday saqlanganini o'qiymiz
const saved = await fetch(`${BASE}/booking/${created.bookingid}`, {
  headers: { Accept: "application/json" },
}).then((r) => r.json());

// 3) Sxema bilan tekshiramiz
if (validate(saved)) {
  console.log("✅ Javob sxemaga mos");
} else {
  console.log("❌ Sxema xatolari:");
  for (const err of validate.errors) {
    console.log(`  ${err.instancePath || "/"} ${err.message}`);
  }
}

Haqiqiy natija (node validate-booking.mjs):

❌ Sxema xatolari:
  /firstname must NOT have fewer than 1 characters
  /totalprice must be >= 0
  /bookingdates/checkin must match format "date"
  /bookingdates/checkout must match format "date"

Bitta sxema to'rtta bug'ni birdaniga ushladi. Shuning uchun sxema tekshiruvi — API regression'ining arzon va kuchli qatlami.

Postman'da

const schema = {
  type: "object",
  required: ["bookingid", "booking"],
  properties: {
    bookingid: { type: "integer" },
    booking: {
      type: "object",
      required: ["firstname", "totalprice", "bookingdates"],
      properties: {
        firstname: { type: "string" },
        totalprice: { type: "integer" },
        bookingdates: {
          type: "object",
          properties: {
            checkin: { type: "string", format: "date" },
            checkout: { type: "string", format: "date" },
          },
        },
      },
    },
  },
};

pm.test("Javob JSON Schema'ga mos", () => {
  pm.response.to.have.jsonSchema(schema);
});

Yaroqsiz sana bilan booking yaratilganda Newman 6.2.3 aynan shunday xato beradi:

AssertionError  Javob JSON Schema'ga mos
expected data to satisfy schema but found following errors:
data.booking.bookingdates.checkin should match format "date"

⚠️ Diqqat: Newman 6.2.3 (Postman sandbox) bilan tekshirganimizda sxemaga $schema: "https://json-schema.org/draft/2020-12/schema" qo'shilsa, test no schema with key or ref ... xatosi bilan yiqildi; $schema umuman yozilmaganda yoki draft-07 ko'rsatilganda ishladi. Postman skriptlarida $schemani yozmang — ishlatayotgan versiyangizda alohida sinab ko'ring.

Sxemani qayerdan olamiz? Agar API OpenAPI hujjatiga ega bo'lsa, sxemalar uning components.schemas bo'limida tayyor turadi — ularni qo'lda qayta yozmang, hujjatdan oling. Shunda test hujjat va kod mosligini ham tekshiradi (11-bo'lim).


9. Autentifikatsiya va avtorizatsiya testlari {#auth-tests}

Token holatlari matritsasi

HolatKutilganToolshop'da tekshirildi
Token yo'q401✅ 401 {"message":"Unauthorized"}
Buzilgan token (oxirgi belgi o'zgartirilgan)401✅ 401
Muddati o'tgan token (Toolshop'da 5 daqiqa)401Mashq sifatida o'zingiz tekshiring
Logout'dan keyingi token401✅ 200 → logout → 401
To'g'ri token, ruxsat yo'q rol403✅ xaridor → GET /users → 403
To'g'ri token, o'z resursi200✅ GET /users/me → 200
To'g'ri token, boshqa foydalanuvchining resursi403 yoki 404IDOR — pastga qarang

Rollar matritsasi (DemoBozor, xayoliy)

EndpointMehmonXaridor AXaridor BSotuvchiAdmin
GET /products✅✅✅✅✅
GET /orders/{A ning buyurtmasi}401✅403/404403\*✅
POST /orders/{id}/cancel (A ning, PAID)401✅403/404403✅
PATCH /products/{id} (sotuvchining o'z mahsuloti)401403403✅✅
GET /admin/users401403403403✅

\* Talabga bog'liq: sotuvchi o'z mahsuloti bor buyurtmani ko'rishi mumkin bo'lsa — faqat o'sha qismini.

IDOR / BOLA — API'dagi eng xavfli bug turi

IDOR (Insecure Direct Object Reference), OWASP API Security Top 10 (2023) da API1: Broken Object Level Authorization (BOLA) — server "bu foydalanuvchi shu obyektga egami?" deb tekshirmaydi. Xaridor B o'z token'i bilan GET /api/v1/orders/58231 (A ning buyurtmasi) so'rasa va javob 200 bo'lsa — begona manzil, telefon va xaridlar ochilib qoladi.

Test usuli oddiy: ikki test foydalanuvchi, A ning resurs ID'sini B ning token'i bilan so'rash — har bir endpoint uchun. Batafsil va boshqa xavfsizlik testlari: QA 24.

🎯 Intervyuda: "API'da avtorizatsiyani qanday testlaysiz?" — Rollar × endpoint'lar matritsasi tuzaman; har bir rol uchun ruxsat etilgan va taqiqlangan amallarni tekshiraman; ikki foydalanuvchi bilan bir-birining resurslariga kirishga urinaman (IDOR/BOLA); token yo'q, buzilgan, muddati o'tgan va logout'dan keyingi holatlarni alohida tekshiraman.


10. Mock'lash: back-end tayyor bo'lmaganda {#mocking}

Mock — haqiqiy server o'rniga oldindan tayyorlangan javob qaytaradigan "soxta" server yoki qatlam. Qachon kerak:

  • back-end hali tayyor emas, lekin front-end/mobil ilovani testlash kerak;
  • qiyin takrorlanadigan holatlar: 500, timeout, bo'sh ro'yxat, 99 ta element, null narx;
  • uchinchi tomon xizmati (to'lov, SMS) test muhitida yo'q yoki pullik.
VositaQayerda ishlaydiQachon qulay
Postman Mock ServersPostman bulutidaCollection'dagi saqlangan misollar asosida tez mock
MockoonDesktop ilova + CLILokal, kod yozmasdan, jamoa bilan fayl orqali bo'lishish
WireMockJava/standalone/DockerMurakkab qoidalar, CI'da avtomatik testlar
Chrome Local overridesBrauzerWeb'da bitta javobni tez almashtirish (QA 9)
Proxyman Map LocalMac/iOS/Android trafigiMobil ilova javoblarini almashtirish (QA 15)
Playwright page.routeAvtomatik UI testlarTestning o'zida javobni mock qilish (QA 19)

⚠️ Diqqat: Mock — haqiqatning nusxasi, haqiqatning o'zi emas. Back-end o'zgarib, mock eskicha qolsa, testlar yashil, production esa qizil bo'ladi. Buning davosi — contract testing.


11. Contract testing {#contract}

Contract (shartnoma) — client va server o'rtasida "qanday so'rovga qanday javob" kelishuvi. Contract testing shu kelishuv buzilmaganini avtomatik tekshiradi.

Ikki asosiy yondashuv:

  1. Consumer-driven contracts (Pact). Client (consumer) testlari Pact yaratgan mock provider'ga so'rov yuboradi; natijada pact fayli — shartnoma yoziladi. Keyin provider (server) tomonida Pact shu so'rovlarni haqiqiy serverga qayta yuboradi va javob shartnomada kutilgan minimal ma'lumotni o'z ichiga olishini tekshiradi. Pact Broker shartnomalarni jamoalar va CI o'rtasida bo'lishish uchun ishlatiladi.
  2. Schema-based (OpenAPI). Server javoblari OpenAPI hujjatidagi sxemaga mosligini tekshirish (yuqoridagi JSON Schema yondashuvining kengaytmasi).

Shartnoma buzilishiga real misollar (2026-yil oktabr):

  • Toolshop: OpenAPI hujjatida GET /users uchun faqat 200, 400, 401 javoblari yozilgan, amalda esa xaridor token'i bilan 403 keladi. POST /users/login uchun faqat 200 hujjatlangan, noto'g'ri parolda esa 401 qaytadi. Hujjat to'liq emas.
  • Swagger Petstore: hujjat noto'g'ri ID uchun 400 deydi, server 404 qaytaradi (QA 10).

Bunday "drift" mobil ilovalar uchun ayniqsa xavfli: eski versiyadagi ilovalar foydalanuvchilarda oylab ishlaydi va server javobi o'zgarsa, ular qulashi mumkin.

Test piramidasida contract testlar unit va integration testlar orasida turadi: tez, lekin ikki tomon o'rtasidagi kelishuvni tekshiradi. Ularni odatda dasturchilar yoki SDET'lar yozadi; QA'ning vazifasi — qaysi endpoint'lar va maydonlar "shartnoma"ga kirishi kerakligini aniqlashda qatnashish va drift'larni bug sifatida ko'tarish.


12. GraphQL, WebSocket, SSE va gRPC — QA uchun farqlar {#other-apis}

GraphQL

GraphQL'da odatda bitta endpoint (POST /graphql) bo'ladi; client aynan qaysi maydonlar kerakligini so'rovda yozadi (query — o'qish, mutation — o'zgartirish).

QA uchun eng muhim tuzoq — xato ko'pincha HTTP 200 bilan qaytadi. Ochiq GraphQL API'da tekshirdik:

# To'g'ri so'rov
curl -s -X POST https://countries.trevorblades.com/graphql -H 'Content-Type: application/json' \
  -d '{"query":"{ country(code: \"UZ\") { name capital currency } }"}'
# 200 {"data":{"country":{"capital":"Tashkent","currency":"UZS","name":"Uzbekistan"}}}

# Mavjud bo'lmagan maydon
curl -s -w "\n%{http_code}\n" -X POST https://countries.trevorblades.com/graphql -H 'Content-Type: application/json' \
  -d '{"query":"{ country(code: \"UZ\") { name population } }"}'
# {"errors":[{"message":"Cannot query field \"population\" on type \"Country\".", ...}]}
# 200

# Mavjud bo'lmagan davlat kodi
curl -s -w "\n%{http_code}\n" -X POST https://countries.trevorblades.com/graphql -H 'Content-Type: application/json' \
  -d '{"query":"{ country(code: \"XX\") { name } }"}'
# {"data":{"country":null}}
# 200

GraphQL-over-HTTP spetsifikatsiyasining yangi qoralamasi validatsiya xatolari uchun 422 ni tavsiya qiladi, lekin amalda serverlar har xil ishlaydi. Xulosa: GraphQL'da faqat statusga ishonmang — har doim errors massivini tekshiring:

const json = pm.response.json();

pm.test("HTTP 200", () => {
  pm.response.to.have.status(200);
});

pm.test("GraphQL xatosi yo'q", () => {
  pm.expect(json.errors, JSON.stringify(json.errors)).to.be.undefined;
});

pm.test("Davlat nomi to'g'ri", () => {
  pm.expect(json.data.country.name).to.eql("Uzbekistan");
});

Noto'g'ri maydonli so'rovda bu skriptning birinchi testi o'tdi (status 200), ikkinchisi esa xatoni ushladi — aynan shu sababli errors tekshiruvi shart. GraphQL uchun qo'shimcha test g'oyalari: juda chuqur ichma-ich so'rovlar (server cheklaydimi), katta first: 100000 kabi limitlar, introspection'ning production'da yopiqligi, mutation'larda avtorizatsiya.

WebSocket va SSE

  • WebSocket — client va server o'rtasida doimiy, ikki tomonlama ulanish (chat, jonli buyurtma holati). Ulanish 101 Switching Protocols bilan ochiladi. Test: ulanishda autentifikatsiya, xabarlar tartibi, ulanish uzilib qayta tiklanishi (reconnect), bir vaqtda ko'p xabar. Chrome DevTools → Network → WS filtri → Messages tab'i; Postman ham WebSocket so'rovlarini qo'llab-quvvatlaydi.
  • SSE (Server-Sent Events) — faqat serverdan client'ga oqim (Content-Type: text/event-stream), masalan, "buyurtmangiz yo'lda" bildirishnomalari. Test: ulanish uzilganda qayta ulanish va o'tkazib yuborilgan hodisalar.

gRPC

gRPC — Protocol Buffers (binary format) va HTTP/2 ustida ishlaydi; mikroservislar orasida ko'p uchraydi. O'z status kodlari bor (OK, INVALID_ARGUMENT, NOT_FOUND, PERMISSION_DENIED, UNAUTHENTICATED va boshqalar). Testlash uchun Postman'ning gRPC client'i yoki grpcurl kabi CLI vositalar ishlatiladi; .proto fayl — gRPC'ning "shartnomasi".


13. API test checklisti {#api-checklist}

SohaTekshiruv
HujjatHar endpoint hujjatlangan; misollar ishlaydi; javob kodlari to'liq
StatusHar holat uchun to'g'ri kod; 500 faqat haqiqiy kutilmagan holatda
Header'larContent-Type; 201 da Location; 405 da Allow; 401 da WWW-Authenticate; keshlash; x-powered-by kabi ortiqcha ma'lumot yo'q
SxemaMajburiy maydonlar, tiplar, null siyosati, ortiqcha maydonlar
QiymatlarYuborilgan = saqlangan; hisob-kitoblar (summa, chegirma, yetkazish)
ValidatsiyaHar maydon uchun EP/BVA, tiplar, uzunlik, format; jim konvertatsiya yo'q
Biznes qoidalarDemoBozor qoidalari (OTP, promo, savatcha limitlari, holat o'tishlari)
AuthToken yo'q/buzilgan/muddati o'tgan/logout'dan keyin; rollar; IDOR
IdempotentlikTakroriy so'rovlar, Idempotency-Key, ikki marta bosish
ParallelRace condition: oxirgi mahsulot, bir martalik promo
SahifalashChegaralar, total mosligi, dublikat/tushib qolish, saralash barqarorligi
Rate limit429, Retry-After, blokdan chiqish
Xato formatiIzchil tuzilma, maydon darajasidagi xatolar, ichki ma'lumot yo'q
Yon ta'sirlarDB, keyingi GET, email/SMS/push, audit log
PerformanceAsosiy endpoint'lar javob vaqti kelishilgan chegarada (QA 23)
ContractJavob OpenAPI/pact bilan mos; eski mobil versiyalar buzilmaydi

14. Amaliy mashqlar {#mashqlar}

Mashq 1: DemoBozor promo-kod endpoint'i uchun negative testlar

Xayoliy POST /api/v1/orders body'sida ixtiyoriy promoCode maydoni bor. Qoidalar: 6–12 belgi, A–Z va 0–9, bitta buyurtmaga bitta, minimal summa, muddat, har foydalanuvchi bir marta. Kamida 10 ta negative/chegaraviy test yozing (body'dagi qiymat va kutilgan natija).

<details> <summary>💡 Javobni ko'rish</summary>

#promoCode / holatKutilgan
1"ABC12" (5 belgi)422, format xatosi
2"ABC123" (6)✅ qo'llanadi (agar kod mavjud va amal qilsa)
3"ABCDEFGH1234" (12)✅
4"ABCDEFGH12345" (13)422
5"abc123"422 (yoki hujjatda "katta harfga o'tkaziladi" deyilgan bo'lsa — aynan shu xatti-harakat)
6"ABC 123", "ABC-123"422
7null va maydon yo'q✅ promo'siz buyurtma
8""422 yoki promo'siz — hujjatga ko'ra, lekin 500 emas
9Muddati o'tgan kod422, aniq xabar ("muddati tugagan")
10Minimal summadan kam savatcha422, "minimal summa ..."
11Bir foydalanuvchi ikkinchi marta409 yoki 422
12Ikki parallel buyurtmada bir xil bir martalik kodFaqat bittasi muvaffaqiyatli
13"promoCode": ["ABC123", "XYZ789"] (massiv)422 — bitta buyurtmaga bitta kod
14Mavjud kod + ataylab "discount": 100 maydoniChegirma faqat serverda hisoblanadi

</details>

Mashq 2: Topilmalarni tasniflang

Restful-booker kuzatuvlarini bug / hujjatga mos, lekin odatga zid / to'g'ri deb tasniflang va bug'larga jiddiylik bering: (a) DELETE → 201; (b) totalprice: 450.75 → 450; (c) buzilgan JSON → 400; (d) mavjud bo'lmagan ID'ga DELETE → 405; (e) "isAdmin": true e'tiborsiz qoldirildi; (f) GET /booking?checkin=2026-13-99 → 500.

<details> <summary>💡 Javobni ko'rish</summary>

TasnifIzoh
aHujjatga mos, odatga zidAPI dizayni masalasi, past ustuvorlik
bBug, MajorPul summasi jimgina o'zgaradi — ma'lumot buzilishi
cTo'g'riKutilgan xatti-harakat
dBug, Minor404 kutiladi; 405 uchun Allow header'i ham yo'q
eTo'g'riMass assignment'ga qarshi to'g'ri xatti-harakat (yaxshi belgi)
fBug, MajorFoydalanuvchi kiritgan filtr serverni yiqitadi; 400 kutiladi

</details>

Mashq 3: Toolshop mahsuloti uchun JSON Schema

GET https://api.practicesoftwaretesting.com/products?page=1 javobidagi bitta mahsulot uchun sxema yozing: id va name — bo'sh bo'lmagan string, price — musbat son, in_stock — boolean, category va brand — id, name maydonli obyektlar. So'ng barcha sahifalardagi mahsulotlarni tekshiring.

<details> <summary>💡 Javobni ko'rish</summary>

// validate-products.mjs — Toolshop mahsulotlar ro'yxatini sxema bilan tekshirish
import Ajv2020 from "ajv/dist/2020.js";

const productSchema = {
  type: "object",
  required: ["id", "name", "price", "in_stock", "category", "brand"],
  properties: {
    id: { type: "string", minLength: 1 },
    name: { type: "string", minLength: 1, maxLength: 120 },
    price: { type: "number", exclusiveMinimum: 0 },
    in_stock: { type: "boolean" },
    is_rental: { type: "boolean" },
    category: {
      type: "object",
      required: ["id", "name"],
      properties: { id: { type: "string" }, name: { type: "string" } },
    },
    brand: {
      type: "object",
      required: ["id", "name"],
      properties: { id: { type: "string" }, name: { type: "string" } },
    },
  },
};

const pageSchema = {
  type: "object",
  required: ["current_page", "data", "last_page", "per_page", "total"],
  properties: {
    current_page: { type: "integer", minimum: 1 },
    per_page: { type: "integer", minimum: 1 },
    total: { type: "integer", minimum: 0 },
    data: { type: "array", items: { $ref: "#/$defs/product" } },
  },
  $defs: { product: productSchema },
};

const ajv = new Ajv2020({ allErrors: true });
const validate = ajv.compile(pageSchema);

let checked = 0;
for (let page = 1; page <= 6; page++) {
  const body = await fetch(
    `https://api.practicesoftwaretesting.com/products?page=${page}`,
  ).then((r) => r.json());
  checked += body.data.length;
  if (!validate(body)) {
    console.log(
      `❌ ${page}-sahifa:`,
      validate.errors.map((e) => `${e.instancePath} ${e.message}`),
    );
  }
}
console.log(`Tekshirilgan mahsulotlar: ${checked}`);

2026-yil oktabrda natija: Tekshirilgan mahsulotlar: 50, sxema xatolari yo'q. Keyingi qadam: sxemani OpenAPI hujjatidagi components.schemas bilan solishtiring — farq bo'lsa, bu ham topilma.

</details>

Mashq 4: Avtorizatsiya matritsasi

DemoBozor'ning GET /api/v1/orders/{id} va POST /api/v1/orders/{id}/cancel endpoint'lari uchun test matritsasi tuzing: mehmon, buyurtma egasi, boshqa xaridor, sotuvchi, admin, muddati o'tgan token. cancel uchun holat qoidalarini ham hisobga oling.

<details> <summary>💡 Javobni ko'rish</summary>

KimGET /orders/{id}POST /orders/{id}/cancel
Mehmon (token yo'q)401401
Muddati o'tgan token401401
Buyurtma egasi200CREATED/PENDING_PAYMENT/PAID/PACKING da 200 (PAID'da pul qaytarish boshlanadi); SHIPPED/DELIVERED da 409/422
Boshqa xaridor403 yoki 404 (ID mavjudligini oshkor qilmaslik uchun 404 afzal)403/404, buyurtma holati o'zgarmaydi
SotuvchiTalabga ko'ra (faqat o'z mahsulotlari qismi)403
Admin200Talabga ko'ra, audit log yoziladi

Qo'shimcha: har bir rad etilgan urinishdan keyin GET bilan buyurtma holati o'zgarmaganini tekshiring (yon ta'sir!).

</details>

Mashq 5: GraphQL'da "yashil" yolg'on

Nega GraphQL API uchun faqat pm.response.to.have.status(200) testi yetarli emas? Countries API misolida qaysi so'rovlar 200 qaytarib, aslida muammoli? Ularni ushlaydigan testni yozing.

<details> <summary>💡 Javobni ko'rish</summary>

Countries API'da: mavjud bo'lmagan maydon → 200 + errors; mavjud bo'lmagan davlat kodi → 200 + "country": null. Ikkalasi ham faqat status bo'yicha "yashil".

const json = pm.response.json();

pm.test("GraphQL xatosi yo'q", () => {
  pm.expect(json.errors, JSON.stringify(json.errors)).to.be.undefined;
});

pm.test("Davlat topildi", () => {
  // .to.exist — null ham, undefined ham bo'lmasligi kerak
  pm.expect(json.data && json.data.country, "data.country").to.exist;
});

</details>

Mashq 6: Sahifalash test rejasi

Toolshop'da GET /products uchun total: 50, per_page: 9. Har sahifada nechta element bo'lishi kerak? Kamida 6 ta test case yozing (kutilgan natija bilan).

<details> <summary>💡 Javobni ko'rish</summary>

ceil(50 / 9) = 6 sahifa: 1–5-sahifalarda 9 tadan (45 ta), 6-sahifada 5 ta (46–50), 7-sahifa bo'sh.

#So'rovKutilgan
1page=19 element, from=1, to=9
2page=65 element, from=46, to=50
3page=7data: [], 200 (2026-yil oktabrda shunday)
4Barcha sahifalar ID'lari50 ta noyob ID, dublikat yo'q
5page=0, page=-1, page=abcHujjatlangan xatti-harakat; amalda jimgina 1-sahifa — talab bilan solishtiring
6sort=price,asc bilan 1→6 sahifalarBarcha 50 ta narx o'sish tartibida, sahifa chegaralarida ham (N-sahifaning oxirgisi ≤ N+1-sahifaning birinchisi) — 2026-yil oktabrda shunday
7sort=noto'g'ri,qiymat400 kutiladi; amalda 500 — bug

</details>


Xulosa {#xulosa}

  • "Status 200" — testning boshlanishi, oxiri emas: sxema, qiymatlar, biznes qoidalar va yon ta'sirlarni ham tekshiring.
  • Har maydon uchun mavjudlik, tip, chegara, format, Unicode va "server hal qiladigan" qiymatlar (narx, rol) — tizimli negative test manbai.
  • Eng xavfli API bug'lari jim bo'ladi: "abc" → null, 450.75 → 450, yaroqsiz sana → "0NaN-aN-aN". Ularni sxema va qiymat tekshiruvlari ushlaydi.
  • JSON Schema bitta testda bir nechta bug'ni ushlaydi; Postman'da $schemani yozmang, Node'da Ajv 2020 bilan draft 2020-12 ishlating.
  • 401 = autentifikatsiya, 403 = avtorizatsiya; rollar matritsasi va IDOR/BOLA testlari majburiy.
  • Mock'lar tezlik beradi, contract testing esa mock va haqiqat ajralib ketmasligini kafolatlaydi.
  • GraphQL'da xato ko'pincha 200 ichida keladi — errors massivini tekshiring.

Keyingi dars: QA 12: SQL va ma'lumotlar bazasi — QA uchun SELECT, JOIN, GROUP BY va data tekshiruvi

Manbalar {#manbalar}

  1. RFC 9110 — HTTP Semantics
  2. RFC 9457 — Problem Details for HTTP APIs
  3. MDN — 422 Unprocessable Content
  4. MDN — 429 Too Many Requests
  5. MDN — Retry-After
  6. MDN — 405 Method Not Allowed
  7. MDN — WWW-Authenticate
  8. JSON Schema — Understanding JSON Schema
  9. JSON Schema — Draft 2020-12
  10. JSON Schema — type keyword
  11. Ajv JSON schema validator
  12. Ajv — JSON Schema drafts support
  13. Postman Docs — Test script examples
  14. Postman Docs — Set up mock servers
  15. Postman Docs — WebSocket requests
  16. Postman Docs — gRPC client
  17. OWASP API Security Top 10 (2023)
  18. OWASP — API1:2023 Broken Object Level Authorization
  19. OWASP — API3:2023 Broken Object Property Level Authorization
  20. OWASP — API4:2023 Unrestricted Resource Consumption
  21. Pact — How Pact works
  22. Pact — What is Pact good for?
  23. OpenAPI Specification (latest)
  24. Mockoon va Mockoon CLI
  25. WireMock — Documentation
  26. Proxyman — Map Local
  27. Playwright — Mock APIs
  28. GraphQL — Learn
  29. GraphQL Specification (October 2021) — Errors
  30. GraphQL over HTTP (draft)
  31. Countries GraphQL API — GitHub
  32. MDN — WebSockets API
  33. MDN — Server-sent events
  34. gRPC va gRPC status codes
  35. Restful-booker API hujjati
  36. Toolshop (Practice Software Testing) — GitHub
  37. Toolshop API hujjati

Markdown manba

iOS Lessons