Home > Back-end >  Tests for view names failing since upgrading to Django 4.0
Tests for view names failing since upgrading to Django 4.0

Time:12-16

In a Django project I have tests that check that a URL uses a specific class-based view. e.g. I have this view:

from django.views.generic import TemplateView

class HomeView(TemplateView):
    template_name = "home.html"

And this test:

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__)

This test passes in Django 3.x but once I try it with Django 4.0 the test fails with:

self.assertEqual(resolve("/").func.__name__, views.HomeView.__name__)
AssertionError: 'view' != 'HomeView'
- view
  HomeView

Obviously something has changed in Django 4.0 but I can't see anything related in the release notes.

So, what's changed and how can I make this test work again (or how can I test this in a better way)?

CodePudding user response:

You use .as_view(), it thus does not return a class object, but a function that will handle requests by creating a new HomeView and trigger the corresponding method based on the HTTP method.

You can check if it is a method of the HomeView by checking the .view_class attribute. Furthermore it is probably better to check if the two classes are the same, not the names of these classes:

#                                            ↓ no .__name__  ↓
self.assertEqual(resolve('/').func.view_class, views.HomeView)

I did some research why comparing the __name__s no longer works. Apparently in they used the update_wrapper(…) function which sets the __name__, __qualname__, etc. of one object (in this case the class) to the function. Indeed, in the source code [GitHub], we can see:

@classonlymethod def as_view(cls, **initkwargs):
    # …
    def view(request, *args, **kwargs):
        # …
    # …

    # take name and docstring from class
    update_wrapper(view, cls, updated=())
    # …

this has been changed in where they also explained in a comment why they do no longer do that [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.
    # …
  • Related