Тесты для имен представлений не работают после обновления до Django 4.0
В проекте Django у меня есть тесты, которые проверяют, что URL использует определенное представление на основе класса. Например, у меня есть такое представление:
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = "home.html"
А этот тест:
from django.test import TestCase
from django.urls import resolve
from myapp import views
class UrlsTestCase(TestCase):
def test_home_view(self):
self.assertEqual(resolve("/").func.__name__, views.HomeView.__name__)
Этот тест проходит в Django 3.x, но когда я пробую его с Django 4.0, тест не проходит:
self.assertEqual(resolve("/").func.__name__, views.HomeView.__name__)
AssertionError: 'view' != 'HomeView'
- view
+ HomeView
Очевидно, что-то изменилось в Django 4.0, но я не вижу ничего связанного с этим в примечаниях к релизу.
Итак, что изменилось и как я могу заставить этот тест работать снова (или как я могу проверить это лучшим способом)?
Вы используете .as_view()
, таким образом возвращается не объект класса, а функция, которая будет обрабатывать запросы, создавая новый HomeView
и вызывая соответствующий метод на основе HTTP-метода.
Вы можете проверить, является ли это методом класса HomeView
, проверив атрибут .view_class
. Кроме того, вероятно, лучше проверять, одинаковы ли два класса, а не имена этих классов:
# ↓ no .__name__ ↓
self.assertEqual(resolve('/').func.view_class, views.HomeView)
Я провел небольшое исследование, почему сравнение __name__
больше не работает. Очевидно, в django-3.0 они использовали update_wrapper(…)
функцию , которая устанавливает __name__
, __qualname__
и т.д. одного объекта (в данном случае класса) в функцию. Действительно, в исходном коде [GitHub] мы можем увидеть:
@classonlymethod def as_view(cls, **initkwargs): # … def view(request, *args, **kwargs): # … # … # take name and docstring from class update_wrapper(view, cls, updated=()) # …
это было изменено в django-4.0, где они также объяснили в комментарии, почему они больше не делают этого [GitHub]:
@classonlymethod def as_view(cls, **initkwargs): # … # __name__ and __qualname__ are intentionally left unchanged as # view_class should be used to robustly determine the name of the view # instead. # …