[英]Assertion error parsing response to POST request from DRF viewset with DRF APIClient in tests
我在使用 Django Rest 框架測試引擎時遇到了一個奇怪的問題。 奇怪的是,以前在 Django 3 上一切正常,而在我遷移到 Django 4 后出現了這個問題。除了測試之外,一切正常,並按預期響應查詢。
問題
我正在使用 DRF APIClient 對單元測試進行查詢。 雖然 GET 請求可以按預期執行,但我無法使 POST 請求正常工作。
這是我為解決問題而創建的一些簡約示例代碼。 我正在使用的版本:
Python 3.9
Django==4.0.3
djangorestframework==3.13.1
from django.db import models
from django.urls import include, path
from django.utils import timezone
from rest_framework import routers, serializers, viewsets
router = routers.DefaultRouter()
# models.py
class SomeThing(models.Model):
created_at = models.DateTimeField(default=timezone.now)
title = models.CharField(max_length=100, null=True, blank=True)
# serializers.py
class SomeThingSerializer(serializers.ModelSerializer):
class Meta:
fields = "__all__"
model = SomeThing
# views.py
class SomeThingViewSet(viewsets.ModelViewSet):
queryset = SomeThing.objects.all().order_by('id')
serializer_class = SomeThingSerializer
# urls.py
router.register("some-things", SomeThingViewSet, basename="some_thing")
app_name = 'question'
urlpatterns = (
path('', include(router.urls)),
)
這是我的測試用例:
import json
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase, APIClient
class TestUserView(APITestCase):
self.some_user = get_user_model().objects.create(login="some_user@test.ru")
@staticmethod
def get_client(user):
client = APIClient()
client.force_authenticate(user=user)
return client
def test_do_something(self):
client = self.get_client(self.compliance_chief)
url = reverse('question:some_things-list')
resp = client.post(
path=url,
data=json.dumps({"title": "Created Something"}),
content_type="application/json",
)
assert resp.status_code == status.HTTP_201_OK
(是的,我必須使用一些身份驗證來訪問數據,但我認為這與問題無關。)我收到了一個冗長的回溯,以斷言錯誤結束:
File "/****/****/****/venv/lib/python3.9/site-packages/django/test/client.py", line 82, in read
assert (
AssertionError: Cannot read more than the available bytes from the HTTP incoming data.
由於它真的很長,我會留下它以防萬一,而不在這里發布。
修復步驟
在視圖集返回正確的響應后,問題顯然發生了。 為了確保響應正確,我在 create 方法中做了一些定制,以便在返回之前打印出響應,如下所示:
class SomeThingViewSet(viewsets.ModelViewSet):
queryset = SomeThing.objects.all().order_by('id')
serializer_class = SomeThingSerializer
def create(self, request, *args, **kwargs):
response = super().create(request, *args, **kwargs)
print("THIS IS THE RESPONSE FROM THE VIEWSET", response)
return response
而且,果然,結果是正確的:
THIS IS THE RESPONSE FROM THE VIEWSET <Response status_code=201, "text/html; charset=utf-8">
這讓我覺得在解析階段出了點問題(實際上,回溯意味着相同)。 我試圖調整構建查詢的方式,即:
resp = client.post(path=url, data={"title": "Created Something"}, format="json")
resp = client.generic(method="POST", path=url, data=json.dumps({"title": "Created Something"}), content_type="application/json")
結果是一樣的。
通過谷歌搜索,我發現這個錯誤確實偶爾發生在與 DRF APIClient 和 Django 相關的情況下,但很久以前(就像這個討論,它聲稱該問題已在更高版本的 Django 中得到修復)。
我確信這種行為的原因是相當明顯的(最有可能是一些愚蠢的錯誤)並且解決方案必須非常簡單,但到目前為止我還沒有找到它。 如果有人分享他們處理此類問題的經驗(如果有的話),或者他們對如何擺脫這種僵局的考慮,我將不勝感激。
好吧,這個謎已經解決了,我將在這里分享它,以防有人遇到類似的事情,雖然這需要相當巧合,所以不太可能。
長話短說:我弄亂了我的 Django4.0.3 的源代碼。 安裝在這個項目中。
現在,它是如何發生的。 當我測試一些東西時,我遇到了一個錯誤,我沒有找到,所以我沿着整個事件鏈檢查 output 是否符合我的預期。 很快,我發現自己從安裝在我的虛擬環境下的庫中的函數中檢查了 output。 我意識到直接修改他們的代碼是一種弊端,但是當我在本地環境中工作時可以選擇隨時重新安裝所有內容時,我認為可以與他們一起玩。 由於沒有任何結果,我刪除了我添加的所有代碼(或者我認為是這樣)。
過了一會兒,我意識到是什么導致了初始錯誤(我的測試設置中被忽略的情況),修復了它並嘗試運行測試。 這時候問題就出現了。
后來我發現同樣的測試在相同的環境中也能正確執行。 然后我懷疑我破壞了本地庫代碼中的某些內容。 接下來,我只是將我在本地環境中處理的代碼與官方來源的代碼進行了比較,很快我就建立了違規行。 它恰好在 django/test/client.py 中,在RequestFactory.generic
方法的定義中。 像這樣的東西:
...
if not r.get("QUERY_STRING"):
# WSGI requires latin-1 encoded strings. See get_path_info().
query_string = parsed[4].encode().decode("iso-8859-1")
r["QUERY_STRING"] = query_string
req = self.request(**r)
return self.request(**r)
...
違規行(我添加並忘記刪除)是req = self.request(**r)
。 刪除后,一切恢復正常。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.