Darslik · 2026-08-16 · ~7 daqiqa o‘qiladi · boshlang'ich
DRF API'ni APIClient bilan testlash: status, JSON va ruxsatlar
Mundarija
Seriyaning birinchi va ikkinchi qismlarida oddiy Django view'largacha yetib keldik. Endi navbat API'da. REST API yozgan odamning odatiy ish tartibi tanish: kod yozdi, Postman'ni ochdi, so'rov yubordi, javobga qaradi. Bu ham aslida "brauzerda bosib ko'rish"ning o'zi — faqat asbob boshqa. Va davosi ham o'sha: bosishni kod qilib yozib qo'yamiz.
Misol sifatida ToDo loyihasidagi API'ning soddalashtirilgan ko'rinishini olamiz:
# apps/todos/serializers.py
from rest_framework import serializers
from apps.todos.models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ["id", "title", "is_done", "due_date"]
# apps/todos/api.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from apps.todos.models import Task
from apps.todos.serializers import TaskSerializer
class TaskViewSet(viewsets.ModelViewSet):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated] # API faqat kirgan foydalanuvchiga
def get_queryset(self):
# har kim faqat o'z vazifalarini ko'radi
return Task.objects.filter(owner=self.request.user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
APIClient: oddiy client'dan nimasi ortiq#
pytest-django bergan client fixture'i HTML sahifalar uchun qulay edi.
API uchun DRF o'zining APIClientini beradi: JSON'ni o'zi seriyalaydi,
format="json" bilan Content-Type'ni to'g'rilaydi va eng foydalisi —
force_authenticate orqali login jarayonisiz "kirgan foydalanuvchi" holatini
beradi. Uni bir marta fixture qilib olamiz, hamma test foydalanadi:
# apps/todos/tests/conftest.py
import pytest
from django.contrib.auth.models import User
from rest_framework.test import APIClient
@pytest.fixture
def user(db):
return User.objects.create_user("olim", password="sinov12345")
@pytest.fixture
def api(user):
"""Login qilgan holdagi API mijoz — testlar tayyor holda oladi."""
client = APIClient()
# sessiya/token bilan ovora bo'lmaymiz: autentifikatsiyani testlash
# alohida mavzu, bu yerda esa API mantig'ini tekshiryapmiz
client.force_authenticate(user=user)
return client
conftest.py — pytest'ning umumiy fixture ombori: shu papkadagi barcha
testlar user va apini parametr sifatida so'rab olaveradi.
To'rt asosiy stsenariy#
API testlari odatda to'rt savol atrofida aylanadi: to'g'ri so'rov to'g'ri javob oladimi, yaroqsiz so'rov to'g'ri rad etiladimi, begona kirolmaydimi va ma'lumot chegarasi buzilmaydimi. Har biriga bittadan yozamiz:
# apps/todos/tests/test_api.py
import pytest
from django.contrib.auth.models import User
from rest_framework.test import APIClient
from apps.todos.models import Task
def test_vazifa_yaratiladi(api):
response = api.post("/api/tasks/", {"title": "Maqola yozish"}, format="json")
assert response.status_code == 201
data = response.json()
assert data["title"] == "Maqola yozish"
assert data["is_done"] is False # default qiymat ham javobda to'g'ri
assert Task.objects.count() == 1 # bazaga haqiqatan yozildi
def test_bosh_sarlavha_rad_etiladi(api):
response = api.post("/api/tasks/", {"title": ""}, format="json")
assert response.status_code == 400
# xato aynan qaysi maydonda ekani ham javobda aytilgan bo'lishi kerak —
# mobil/frontend jamoasi shu maydonga qarab xabar ko'rsatadi
assert "title" in response.json()
assert Task.objects.count() == 0
def test_kirmagan_foydalanuvchi_401_oladi(db):
anonim = APIClient() # force_authenticate YO'Q
response = anonim.get("/api/tasks/")
assert response.status_code == 401
def test_begona_vazifa_korinmaydi(api, db):
begona = User.objects.create_user("g'ani")
Task.objects.create(title="Begonaning ishi", owner=begona)
response = api.get("/api/tasks/")
assert response.status_code == 200
assert response.json() == [] # ro'yxat bo'sh — begonaniki chiqmadi
Oxirgi test alohida e'tiborga loyiq. get_querysetdagi filter(owner=...)
qatori — xavfsizlik chegarasi, va bunday chegara har doim testda mustahkamlab
qo'yilishi kerak: ertaga kimdir refactoring paytida o'sha filtrni yo'qotib
qo'ysa, xato jimgina o'tib ketmaydi — mana shu test qizarib turadi. API'dagi
bunday "meniki-seniki" xatolari eng ko'p uchraydigan zaifliklardan biri,
bu haqda OWASP maqolasida ham yozganman.
Yiqilganda nimani o'qiysiz#
Test yiqilsa, pytest javobning o'zini ham ko'rsatadi — lekin unga bitta yordam qo'shsangiz, hayot yana osonlashadi:
assert response.status_code == 201, response.json()
Assert'ning ikkinchi argumenti xato xabariga qo'shiladi: status kutilgancha chiqmasa, terminalda serializer'ning to'liq xato javobini ko'rasiz — "400 keldi" degan quruq faktni emas, aynan nima noto'g'ri ekanini.
Nomlash bilan hujjat
E'tibor bergan bo'lsangiz, test nomlari yana gap kabi: yaratiladi, rad
etiladi, ko'rinmaydi. To'plam kattalashganda pytest --collect-only
chiqishi o'z-o'zidan API'ning xulq-atvor hujjatiga aylanadi — qaysi
holatda nima bo'lishi ro'yxat bo'lib turadi.
Yakun — va seriya xulosasi#
APIClient bilan test yozish oddiy client'dan deyarli farq qilmaydi, lekin API dunyosiga mos uch odat qo'shiladi: statusni tekshirish, JSON tanasini tekshirish va ruxsat chegaralarini tekshirish. Uchinchisi eng muhimi — funksionallik buzilsa foydalanuvchi shikoyat qiladi, xavfsizlik buzilsa hech kim indamaydi.
Uch qismlik testlash seriyasi shu yerda yakunlanadi: birinchi testdan boshladik, unit va integration muvozanatini ko'rdik, API'ni qamrab oldik. Bu bilimlarning jonli namunasi — ToDo loyihasi, test intizomini o'rganish uchun ataylab qurilgan maydon.