DEV Community

Cover image for Django REST Framework API'si Nasıl Test Edilir
Tobias Hoffmann
Tobias Hoffmann

Posted on • Originally published at apidog.com

Django REST Framework API'si Nasıl Test Edilir

Genellikle DRF olarak kısaltılan Django REST Framework, Django üzerinde API geliştirmek için kullanılan standart araç setlerinden biridir. Serializer, viewset, router, kimlik doğrulama ve izin katmanlarının yanında, Django test çalıştırıcısı üzerine kurulu güçlü bir test altyapısı da sağlar.

Apidog'u bugün deneyin

Bu rehberde bir DRF API’sini iki katmanda test edeceğiz:

  1. Kod seviyesinde otomatik testler: APITestCase ve APIClient ile çalışan, canlı sunucu gerektirmeyen hızlı testler.
  2. Çalışan API üzerinde manuel/otomatik doğrulama: Bir API istemcisiyle gerçek uç noktalara istek göndererek dağıtılmış servisi kontrol etme.

İlk katman regresyonları erken yakalar. İkinci katman ise CORS, proxy, ortam farkları ve gerçek HTTP davranışı gibi yalnızca çalışan sistemde görülen sorunları ortaya çıkarır.

Projeyi ve test ortamını kurun

Önce bağımlılıkları izole etmek için sanal ortam oluşturun:

python -m venv venv
source venv/bin/activate
pip install django djangorestframework coverage
Enter fullscreen mode Exit fullscreen mode

Bir DRF projesinde testler genellikle şu yapılardan birinde tutulur:

articles/
  tests.py
Enter fullscreen mode Exit fullscreen mode

veya test sayısı arttığında:

articles/
  tests/
    __init__.py
    test_serializers.py
    test_views.py
    test_permissions.py
Enter fullscreen mode Exit fullscreen mode

Django test çalıştırıcısı, adı test_ ile başlayan metodları otomatik olarak bulur.

DRF API testleri için temel tercih şu olmalıdır:

from rest_framework.test import APITestCase
Enter fullscreen mode Exit fullscreen mode

APITestCase, Django’nun TestCase sınıfını genişletir ve standart Django test istemcisi yerine DRF’in APIClient istemcisini kullanır. Bu istemci JSON gövdeleri, DRF kimlik doğrulaması, içerik anlaşması ve format="json" gibi DRF’e özgü özellikleri destekler.

Kısa seçim rehberi:

Sınıf Ne zaman kullanılır?
unittest.TestCase Veritabanı gerektirmeyen saf Python testleri
django.test.TestCase Model, servis veya Django davranışı testleri
rest_framework.test.APITestCase DRF endpoint testleri
TransactionTestCase Transaction davranışını gerçekten test etmeniz gerektiğinde

Çoğu API testi için en doğru ve hızlı seçenek APITestCase olacaktır.

Test verisi için iki temel yaklaşım kullanabilirsiniz:

def setUp(self):
    # Her testten önce çalışır
    ...
Enter fullscreen mode Exit fullscreen mode

veya:

@classmethod
def setUpTestData(cls):
    # Sınıf için bir kez çalışır
    ...
Enter fullscreen mode Exit fullscreen mode

Veri salt okunursa setUpTestData daha hızlıdır. Büyük projelerde factory_boy gibi bir fabrika kütüphanesi kullanmak, model alanları değiştiğinde testlerin kırılmasını azaltır.

Serializer'ları birim olarak test edin

Serializer’lar doğrulama yapar ve model verisini JSON’a dönüştürür. HTTP katmanına ihtiyaç duymadıkları için hızlı birim testleri için idealdir.

Örnek bir Article modeli ve ArticleSerializer olduğunu varsayalım:

from django.test import TestCase
from articles.serializers import ArticleSerializer


class ArticleSerializerTests(TestCase):

    def test_valid_data_passes(self):
        data = {
            "title": "Caching strategies",
            "body": "Use ETags.",
        }

        serializer = ArticleSerializer(data=data)

        self.assertTrue(serializer.is_valid())

    def test_missing_title_fails(self):
        data = {
            "body": "No title here.",
        }

        serializer = ArticleSerializer(data=data)

        self.assertFalse(serializer.is_valid())
        self.assertIn("title", serializer.errors)
Enter fullscreen mode Exit fullscreen mode

Bu testler şunları doğrular:

  • Geçerli veri kabul ediliyor mu?
  • Zorunlu alan eksikse hata dönüyor mu?
  • Hata beklenen alana mı bağlı?

Serializer testlerini endpoint testlerinden önce yazmak faydalıdır. Böylece doğrulama kurallarındaki hataları HTTP, routing veya permission katmanına girmeden yakalarsınız. Bu yaklaşım, otomatik test nedir yazısında açıklanan test piramidinin temelini oluşturur.

Uç noktaları APITestCase ve APIClient ile test edin

Endpoint testleri tüm istek döngüsünü kontrol eder:

  • URL routing
  • View veya ViewSet
  • Serializer
  • Permission
  • Response status code
  • Response body
  • Veritabanı etkisi

Örnek:

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from articles.models import Article


class ArticleEndpointTests(APITestCase):

    def setUp(self):
        Article.objects.create(
            title="First post",
            body="Hello world",
        )

    def test_list_articles_returns_200(self):
        url = reverse("article-list")

        response = self.client.get(url)

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)

    def test_create_article(self):
        url = reverse("article-list")
        payload = {
            "title": "Second post",
            "body": "More content",
        }

        response = self.client.post(url, payload, format="json")

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(Article.objects.count(), 2)
Enter fullscreen mode Exit fullscreen mode

Buradaki önemli noktalar:

  • self.client, APITestCase tarafından sağlanan bir APIClient örneğidir.
  • URL’leri elle yazmak yerine reverse("article-list") kullanın.
  • JSON isteklerinde format="json" belirtin.
  • Durum kodlarını çıplak sayılar yerine rest_framework.status sabitleriyle kontrol edin.

Örneğin:

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
Enter fullscreen mode Exit fullscreen mode

şundan daha okunabilirdir:

self.assertEqual(response.status_code, 201)
Enter fullscreen mode Exit fullscreen mode

HTTP durum kodlarını doğru kullanmak için REST API'lerinin kullanması gereken HTTP durum kodları rehberine bakabilirsiniz.

Kimlik doğrulama ve izinleri test edin

Gerçek API’lerin çoğu bazı endpoint’leri korur. Bu yüzden yalnızca başarılı senaryoları değil, reddedilmesi gereken istekleri de test etmelisiniz.

Test etmeniz gereken temel durumlar:

  • Anonim kullanıcı yazma işlemi yapabiliyor mu?
  • Kimliği doğrulanmış kullanıcı izinli işlemi yapabiliyor mu?
  • Yanlış kullanıcı başka bir kullanıcının kaydını değiştirebiliyor mu?
  • Liste endpoint’i yalnızca kullanıcının görmesi gereken kayıtları mı döndürüyor?

DRF APIClient ile iki yaygın yaklaşım vardır:

Yöntem Kullanım amacı
force_authenticate() View ve permission mantığını hızlıca test etmek
login() / credentials() Gerçek auth mekanizmasını test etmek

Örnek izin testi:

from django.contrib.auth.models import User
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase


class ArticlePermissionTests(APITestCase):

    def setUp(self):
        self.user = User.objects.create_user(
            username="editor",
            password="testpass123",
        )
        self.url = reverse("article-list")

    def test_anonymous_cannot_create(self):
        payload = {
            "title": "Blocked",
            "body": "Should fail",
        }

        response = self.client.post(self.url, payload, format="json")

        self.assertEqual(
            response.status_code,
            status.HTTP_403_FORBIDDEN,
        )

    def test_authenticated_user_can_create(self):
        self.client.force_authenticate(user=self.user)

        payload = {
            "title": "Allowed",
            "body": "Should pass",
        }

        response = self.client.post(self.url, payload, format="json")

        self.assertEqual(
            response.status_code,
            status.HTTP_201_CREATED,
        )
Enter fullscreen mode Exit fullscreen mode

force_authenticate() hızlıdır çünkü gerçek kimlik bilgisi akışını çalıştırmaz. Bir kullanıcının belirli bir endpoint’e erişip erişemediğini test etmek için uygundur.

Token kimlik doğrulamasının kendisini test etmek istiyorsanız gerçek header yolunu kullanın:

self.client.credentials(
    HTTP_AUTHORIZATION="Token " + token
)
Enter fullscreen mode Exit fullscreen mode

Örneğin şu durumlar için credentials() daha doğru seçimdir:

  • Geçersiz token reddediliyor mu?
  • Süresi dolmuş token başarısız oluyor mu?
  • Login endpoint’i kullanılabilir token döndürüyor mu?
  • Authorization header eksikse doğru hata dönüyor mu?

Nesne düzeyinde izinleri de açıkça test edin. Örneğin bir kullanıcı yalnızca kendi makalesini düzenleyebiliyorsa:

def test_user_cannot_update_another_users_article(self):
    owner = User.objects.create_user(username="owner", password="pass")
    other_user = User.objects.create_user(username="other", password="pass")

    article = Article.objects.create(
        title="Private",
        body="Owned by another user",
        owner=owner,
    )

    self.client.force_authenticate(user=other_user)

    url = reverse("article-detail", args=[article.id])
    payload = {
        "title": "Changed",
        "body": "Should not be allowed",
    }

    response = self.client.put(url, payload, format="json")

    self.assertIn(
        response.status_code,
        [status.HTTP_403_FORBIDDEN, status.HTTP_404_NOT_FOUND],
    )
Enter fullscreen mode Exit fullscreen mode

Liste endpoint’lerini de kontrol edin. Detay endpoint’i kapalı olsa bile hatalı bir queryset başka kullanıcıların verilerini listeleyebilir.

Test süitini çalıştırın

Tüm testleri Django test çalıştırıcısı ile çalıştırın:

python manage.py test
Enter fullscreen mode Exit fullscreen mode

Belirli bir uygulamanın testlerini çalıştırmak için:

python manage.py test articles
Enter fullscreen mode Exit fullscreen mode

Belirli bir test sınıfı için:

python manage.py test articles.tests.test_views.ArticleEndpointTests
Enter fullscreen mode Exit fullscreen mode

Belirli bir test metodu için:

python manage.py test articles.tests.test_views.ArticleEndpointTests.test_create_article
Enter fullscreen mode Exit fullscreen mode

Django test çalıştırıcısı geçici bir test veritabanı oluşturur, testleri izole şekilde çalıştırır ve sonunda veritabanını temizler. Başarılı çalışmada OK çıktısı görürsünüz.

Kapsama alanını ölçün

Testlerin geçmesi tek başına yeterli değildir. Hangi kodların gerçekten çalıştırıldığını görmek için coverage kullanın:

coverage run --source='.' manage.py test
coverage report
coverage html
Enter fullscreen mode Exit fullscreen mode

Terminal özeti için:

coverage report
Enter fullscreen mode Exit fullscreen mode

HTML raporu açmak için:

open htmlcov/index.html
Enter fullscreen mode Exit fullscreen mode

veya Linux üzerinde:

xdg-open htmlcov/index.html
Enter fullscreen mode Exit fullscreen mode

HTML raporu test edilmeyen satırları kırmızıyla gösterir. Özellikle şu dosyalara bakın:

  • serializers.py
  • views.py
  • permissions.py
  • filters.py
  • services.py
  • models.py

Tek bir yüksek kapsama yüzdesi hedeflemek yerine kritik davranışların test edildiğinden emin olun. Zayıf assertion’lara sahip testler yüksek coverage üretse bile gerçek güven sağlamaz. Daha iyi test yazma yaklaşımı için otomatik test komut dosyaları nasıl yazılır yazısına bakabilirsiniz.

Canlı API'yi bir istemciyle test edin

APITestCase hızlıdır ve CI için uygundur, ancak uygulamayı süreç içinde çalıştırır. Bu nedenle bazı sorunları yakalayamaz:

  • Yanlış CORS yapılandırması
  • Reverse proxy header problemleri
  • Ortam değişkeni farkları
  • Gerçek veritabanı performansı
  • Production/staging ile test veritabanı arasındaki farklar
  • Gateway veya load balancer davranışları

Bu sorunlar için çalışan API’ye gerçek HTTP istekleri göndermeniz gerekir.

Burada Apidog kullanabilirsiniz. Apidog, DRF API’nizin OpenAPI şemasını içe aktarmanıza, çalışan sunucuya istek göndermenize ve görsel olarak doğrulamalar oluşturmanıza olanak sağlayan bir API platformudur.

Tipik akış:

  1. DRF projenizden OpenAPI şemasını üretin.
  2. Şemayı Apidog’a içe aktarın.
  3. Endpoint’leri staging veya local sunucuya karşı çalıştırın.
  4. Login, create, retrieve, update, delete gibi çok adımlı senaryolar oluşturun.
  5. Yanıt status code, body ve header doğrulamalarını tanımlayın.
  6. Bu senaryoları programlı veya CI sürecinde çalıştırın.

Bu yaklaşım APITestCase yerine geçmez. İkisi farklı katmanları korur:

Katman Araç Amaç
Kod içi test APITestCase View, serializer, permission ve routing regresyonlarını yakalamak
Canlı API testi API istemcisi Dağıtılmış servisin gerçek HTTP davranışını doğrulamak

DRF şemasını içe aktarıp denemek için Apidog'u indirebilirsiniz. Python tarafında uçtan uca kalmayı tercih eden ekipler için pytest API otomatik test çerçevesi rehberi de faydalı olabilir.

Sıkça sorulan sorular

TestCase ve APITestCase arasındaki fark nedir?

django.test.TestCase, her testi veritabanı işlemi içinde çalıştırır ve standart Django test istemcisini sağlar. rest_framework.test.APITestCase ise bunu genişletir ve istemci olarak DRF’in APIClient sınıfını kullanır. DRF endpoint’lerini test ederken APITestCase tercih edin.

Login yerine force_authenticate ne zaman kullanmalıyım?

View veya permission davranışını test etmek istiyorsanız force_authenticate() kullanın. Bu yöntem kullanıcıyı doğrudan isteğe bağlar ve hızlıdır.

Kimlik doğrulama mekanizmasının kendisini test ediyorsanız login() veya credentials() kullanın. Örneğin token header’ı, geçersiz token veya login endpoint’i gibi akışları test ederken gerçek kimlik doğrulama yolunu çalıştırmalısınız.

DRF testlerinin çalışan bir sunucuya ihtiyacı var mı?

Hayır. APIClient, istekleri doğrudan Django view katmanına süreç içinde gönderir. Bu nedenle python manage.py runserver çalıştırmanız gerekmez.

Ancak dağıtılmış servisi, proxy’leri, CORS ayarlarını ve gerçek HTTP davranışını test etmek için Apidog gibi bir API istemcisiyle çalışan endpoint’lere istek göndermeniz gerekir.

Bir DRF projesinde test kapsama alanını nasıl kontrol ederim?

Önce coverage paketini kurun:

pip install coverage
Enter fullscreen mode Exit fullscreen mode

Sonra testleri coverage ile çalıştırın:

coverage run --source='.' manage.py test
coverage report
coverage html
Enter fullscreen mode Exit fullscreen mode

HTML raporu, test edilmeyen satırları gösterir. Böylece hangi view, serializer veya permission sınıflarının eksik test edildiğini görebilirsiniz.

Serializer'ları ve endpoint'leri ayrı ayrı test etmeli miyim?

Evet. Serializer testleri hızlıdır ve doğrulama hatalarını HTTP katmanına girmeden yakalar. APITestCase ile yazılan endpoint testleri ise routing, permission, serializer ve response davranışını birlikte doğrular.

Pratik bir test stratejisi şu şekilde olabilir:

  • Serializer doğrulama kuralları için birim testleri
  • ViewSet ve endpoint davranışı için APITestCase
  • Permission ve auth sınırları için ayrı testler
  • Dağıtılmış servis doğrulaması için canlı API testleri

Top comments (0)