Genellikle DRF olarak kısaltılan Django REST Framework, Django üzerinde API'ler oluşturmak için standart bir araç setidir. Size serializer'lar, viewset'ler, router'lar ve bir kimlik doğrulama katmanı sunar. Ayrıca size sunduğu ve birçok geliştiricinin yeterince kullanmadığı şey ise Django'nun kendi test çalıştırıcısı üzerine kurulu sağlam bir test katmanıdır.
Bu rehber, bir DRF API'sini iki farklı şekilde nasıl test edeceğinizi gösterir. İlk olarak, canlı bir sunucu olmadan çalışan ve her committe regresyonları yakalayan, DRF'in APITestCase ve APIClient'ını kullanarak Python'da yazılmış otomatik testler. İkinci olarak, çalışan uç noktaları bir API istemcisi ile kullanmak, bu da davranışı nasıl keşfettiğiniz ve gerçek hizmeti nasıl doğruladığınızdır. Her ikisi de önemlidir ve farklı sorunları yakalarlar.
Projeyi ve test ortamını kurun
Test bağımlılıklarının sisteminizin geri kalanından ayrı kalması için izole bir ortamla başlayın:
python -m venv venv
source venv/bin/activate
pip install django djangorestframework coverage
Bir DRF projesi, herhangi bir Django projesi gibi test edilir. Geleneksel olarak, testler her uygulamanın içinde bir tests.py dosyasında veya birçok testiniz varsa bir tests/ paketinde bulunur. Django'nun test çalıştırıcısı, TestCase varyantını alt sınıf olarak alan herhangi bir sınıfı ve adı test_ ile başlayan herhangi bir metodu keşfeder.
Önemli olan, hangi temel sınıfı kullanacağınızdır. Saf unittest.TestCase'in veritabanı yoktur. Django'nun TestCase'i her testi bir işlem içine alır ve geri döndürür, böylece testler birbirini kirletmez. DRF'in APITestCase'i, Django'nun TestCase'ini genişletir ve DRF kimlik doğrulamasını ve içerik tiplerini anlayan DRF'in APIClient'ı ile değiştirir. API çalışmaları için APITestCase kullanın.
Bilmeye değer bir sınıf daha var. TransactionTestCase, testleri bir işlem içine almaz; buna, test edilen kodun işlemleri kendisi yönettiği veya saran bir işlemin gizleyeceği veritabanı özelliklerine güvendiği durumlarda ihtiyacınız vardır. Geri almak yerine testler arasında tabloları kestiği için daha yavaştır, bu yüzden TestCase davranışı gerçekten modelleyemediğinde yalnızca onu kullanın. Uç nokta testlerinin büyük çoğunluğu için APITestCase doğru ve en hızlı seçimdir.
Test verilerini erken düşünmek de faydalıdır. Bir sınıftaki her testin ihtiyaç duyduğu satırları oluşturmak için bir setUp metodu kullanın veya veri salt okunur ve hız için sınıf genelinde paylaşılabiliyorsa setUpTestData kullanın. Daha büyük projeler için, factory_boy gibi bir fabrika kütüphanesi, her alanı tek tek belirtmeden geçerli model örnekleri oluşturur, bu da bir model yeni zorunlu bir alan kazandığında testleri kısa ve dayanıklı tutar.
Serializer'ları birimler halinde test edin
Serializer'lar doğrulama yapar ve model örnekleri ile JSON arasında dönüşüm sağlar. Küçük ve saf olmaları, uç noktalara dokunmayan hızlı birim testleri için onları ideal kılar.
Bir Article modeliniz ve bir ArticleSerializer'ınız olduğunu varsayalım. Bir birim testi, geçerli verinin geçtiğini ve geçersiz verinin başarısız olduğunu kontrol eder:
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)
Bu testler milisaniyeler içinde çalışır çünkü HTTP ve görünüm yoktur. Uç noktayı düşünmeden önce doğrulama kurallarının doğru olduğunu size söylerler. Bu birim düzeyindeki kapsama, otomatik test nedir yazısında açıklanan test piramidinin temelini oluşturur.
Uç noktaları APITestCase ve APIClient ile test edin
Uç nokta testleri tüm yolu kontrol eder: yönlendirme, görünüm, serializer ve yanıt. DRF'in APIClient'ı, süreç içi istekler gönderir, bu nedenle hiçbir sunucu çalışmaz ve testler hızlı kalır.
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)
Birkaç önemli ayrıntı. self.client, APITestCase tarafından sağlanan bir APIClient örneğidir. URL'leri doğrudan kodlamak yerine rota adıyla reverse() kullanın, böylece bir rota değişikliği her testi bozmaz. Yazma işlemlerinde format="json" parametresini geçirin, böylece istemci yükü doğru şekilde serileştirir. Durum kodunu, ham sayılardan daha net okundukları için rest_framework.status'tan adlandırılmış sabitlerle doğrulayın. Durum kodlarının kendileri, REST API'lerinin kullanması gereken HTTP durum kodları rehberinde ele alınmıştır.
Kimlik doğrulama ve izinleri test edin
Çoğu gerçek API uç noktalarını korur. Kimlik doğrulaması yapılmamış isteklerin reddedildiğini ve doğru izinlere sahip kimliği doğrulanmış isteklerin başarılı olduğunu kanıtlayan testlere ihtiyacınız vardır.
APIClient, bir testi doğrulamak için iki yol sunar. force_authenticate(), kimlik bilgisi kontrolünü atlar ve doğrudan bir kullanıcı ekler, bu da görünüm mantığını test etmek için idealdir. login() veya credentials() gerçek kimlik doğrulama yolunu çalıştırır. İşte her iki yönü de kullanan bir izin testi:
from django.contrib.auth.models import User
from rest_framework import status
from rest_framework.test import APITestCase
from django.urls import reverse
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
)
İlk test, izin sınıfının anonim yazma işlemlerini engellediğini doğrular. İkincisi, kimliği doğrulanmış bir kullanıcının başarılı olduğunu doğrular. Birlikte, yeniden düzenlemeler sırasında sessizce bozulan davranış türü olan izin sınırını belirlerler. Belirli olarak token kimlik doğrulamasını test ediyorsanız, gerçek başlık yolunu çalıştırmak için force_authenticate yerine self.client.credentials(HTTP_AUTHORIZATION="Token " + token) kullanın.
Hangisini kullanacağınız konusunda bilinçli olmak önemlidir. force_authenticate daha hızlıdır ve görünüm mantığını izole eder, bu nedenle bir rolün bir şeyi yapıp yapamayacağını umursadığınız izin testlerinin çoğu için uygundur. Gerçek kimlik bilgisi yolu ise, kimlik doğrulama mekanizmasının çalıştığını özellikle kanıtlayan daha küçük bir test kümesi için istediğiniz şeydir: kötü bir token'ın reddedildiğini, süresi dolmuş bir token'ın başarısız olduğunu, oturum açma uç noktasının kullanılabilir bir token verdiğini. Her ikisini karıştırmak, size uygun fiyata geniş kapsam ve önemli yerlerde derin kapsam sağlar.
Nesne düzeyindeki izinleri unutmayın. Birçok DRF API'si, bir kullanıcının kendi kayıtlarını düzenlemesine izin verir, ancak başkasınınkini değil. Bunu açıkça test edin: iki kullanıcı oluşturun, birinin bir kayıt oluşturmasını sağlayın, ardından diğer kullanıcının onu değiştirmeye çalışırken bir 403 veya 404 aldığını doğrulayın. Liste uç noktaları da aynı incelemeyi hak eder, çünkü sızıntılı bir queryset, ayrıntı uç noktası kilitli olsa bile bir kullanıcının asla görmemesi gereken satırları döndürebilir.
Süiti çalıştırın ve kapsama alanını ölçün
Her testi Django'nun çalıştırıcısı ile çalıştırın:
python manage.py test
Çalıştırıcı, test sınıflarınızı keşfeder, geçici bir test veritabanı kurar, her testi bir işlem içinde çalıştırır ve ardından geri alır. Temiz bir çalışma OK yazdırır. Bir hata, bozulan onaylamayı ve nerede olduğunu gösterir.
Süitin geçtiğini bilmek, yeterince kapsama alanı olduğunu bilmekle aynı şey değildir. coverage aracı hangi satırların gerçekten çalıştığını ölçer:
coverage run --source='.' manage.py test
coverage report
coverage html
Rapor, her dosyanın çalıştırılan satır yüzdesini listeler. HTML çıktısı, test edilmemiş satırları kırmızı renkle vurgular ve sizi doğrudan eksikliklere yönlendirir. Tek bir manşet sayı yerine görünümlerin, serializer'ların ve izinlerin anlamlı bir şekilde kapsanmasını hedefleyin. Zayıf testlerin kapsanması gerçek bir güvenlik olmadığı için, otomatik test komut dosyaları nasıl yazılır hakkındaki yazı, bir testi değerli kılan şeyleri ele alır.
Canlı API'yi bir istemciyle test edin
Otomatik Python testleri hızlıdır ve CI'da çalışır, ancak uygulamayı süreç içinde çalıştırırlar. Çalışan hizmette ortaya çıkan sorunları yakalamazlar: yanlış yapılandırılmış bir CORS başlığı, bir ters proxy'nin bir şeyi çıkarması, gerçek yük altında yavaş bir veritabanı veya test veritabanınız ile üretim arasındaki bir fark. Bunun için dağıtılan uç noktalara gerçek istekler gönderirsiniz.
Burada Apidog güçlü bir uyum sağlar. Hepsi bir arada bir API platformudur, böylece DRF API'nizin OpenAPI şemasını içe aktarabilir, çalışan bir sunucuya canlı istekler gönderebilir ve daha fazla Python kodu yazmadan görsel olarak doğrulamalar oluşturabilirsiniz. DRF bir OpenAPI şeması oluşturabilir ve Apidog bunu doğrudan tüketir, bu da istemcinizi gerçek sözleşmeyle senkronize tutar. Ayrıca çok adımlı test senaryoları oluşturabilir, örneğin oturum açma, bir makale oluşturma, onu alma, silme gibi ve bunları bir program dahilinde veya CI'da çalıştırabilirsiniz. Bu, APITestCase süitinizi tamamlar, yerini almaz: birim ve uç nokta testleri kodu korurken, API istemcisi dağıtılan hizmeti korur. Bir DRF şemasını içe aktarmak ve denemek için Apidog'u indirebilirsiniz. Python'da uçtan uca kalmayı tercih eden ekipler için, pytest API otomatik test çerçevesi rehberi, pytest altında DRF tarzı testlerin nasıl çalıştırılacağını gösterir.
Sıkça sorulan sorular
TestCase ve APITestCase arasındaki fark nedir?
Django'nun TestCase'i her testi bir veritabanı işlemi içine alır ve size standart bir Django test istemcisi sunar. DRF'in APITestCase'i onu alt sınıf olarak alır ve istemciyi, DRF kimlik doğrulama şemalarını, içerik anlaşmasını ve isteklerdeki format argümanını anlayan APIClient ile değiştirir. DRF uç noktalarını test etmek için APITestCase kullanın.
Login yerine force_authenticate ne zaman kullanmalıyım?
Gerçek kimlik bilgisi akışının maliyeti ve karmaşıklığı olmadan görünüm ve izin mantığını test etmek istediğinizde force_authenticate() kullanın. Bu, bir kullanıcıyı doğrudan isteğe ekler. Oturum veya token kimlik doğrulaması gibi kimlik doğrulama mekanizmasının kendisini doğrulamak istediğinizde login() veya credentials() kullanın.
DRF testlerinin çalışan bir sunucuya ihtiyacı var mı?
Hayır. APIClient, istekleri doğrudan görünümlerinize süreç içinde gönderir, bu nedenle test süiti bir sunucu başlatmadan çalışır. Bu da onu hızlı yapar. Gerçekten dağıtılmış hizmeti (proxy'ler ve CORS gibi altyapı dahil) test etmek için, Apidog gibi bir API istemcisiyle gerçek HTTP istekleri gönderirsiniz.
Bir DRF projesi için test kapsama alanını nasıl kontrol ederim?
coverage paketini kurun, ardından bir özet için coverage run --source='.' manage.py test ve ardından coverage report çalıştırın veya satır satır görünüm için coverage html çalıştırın. HTML raporu, test edilmemiş satırları vurgular, böylece hangi görünümlerin veya serializer'ların testlerden yoksun olduğunu tam olarak görebilirsiniz.
Serializer'ları ve uç noktaları ayrı ayrı test etmeli miyim?
Evet. Serializer birim testleri hızlıdır ve HTTP yükü olmadan doğrulama hatalarını tespit eder. APITestCase ile yapılan uç nokta testleri yönlendirmeyi, izinleri ve tam istek döngüsünü kontrol eder. Her ikisini de kullanmak, birim düzeyinde hızlı geri bildirim ve entegrasyon düzeyinde bağlantıların çalıştığına dair güven sağlar.
