1

I use the UUID in the url instead of the primary key. I assume, but am not sure, that this is the cause of my problem in testing my CBVs.

my view for user profile :

class ProfileView(DetailView):
    slug_url_kwarg = 'uuid'
    slug_field = 'uuid'

    model = User
    template_name = 'users/profile.html'
    context_object_name = 'user_profile'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['uuid'] = self.kwargs.get("uuid")
        return context

My url :

path(
    route='profile/<uuid:uuid>',
    view=views.ProfileView.as_view(),
    name='profile',
),

I can't test get_context_data, Django tells me that my view has no "object" attribute. Maybe I need to override get_object, but my search didn't find anything.

My test :

class BaseTest(TestCase):
    def setUp(self):
        # Set up non-modified objects used by all test methods
        self.factory = RequestFactory()
        self.user2 = User.objects.create_user(
            email='caroline.dupont@free.fr',
            password='fhh456GG455t',
            status='VALIDATED',
            )
    
        return super().setUp()

    def profile_view_instance(self, test_user):
        request = self.factory.get(reverse('profile', args=(test_user.uuid,)))
        request.user = test_user
        view = ProfileView()
        view.setup(request)
    
        return view

class ProfileViewTestCase(BaseTest):

    def test_get_context_data(self):
        self.client.force_login(self.user2)
        context = self.profile_view_instance(self.user2).get_context_data()
        self.assertIn('uuid', context)

The error :

ERROR: test_get_context_data (tests.appusers.test_views.ProfileViewTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\Developpement\projet13\tests\appusers\test_views.py", line 75, in test_get_context_data
    context = self.profile_view_instance(self.user2).get_context_data()
  File "D:\Developpement\projet13\users\views.py", line 66, in get_context_data
    context = super().get_context_data(**kwargs)
  File "D:\Developpement\projet13\venvp13\lib\site-packages\django\views\generic\detail.py", line 94, in get_context_data
if self.object:
AttributeError: 'ProfileView' object has no attribute 'object'
  • I advise to use the client tool https://docs.djangoproject.com/en/3.2/topics/testing/tools/ You here trigger view logic, but you skip certain steps. For example the `.get(...)` method will assign a `.object` attribute to the object the view shares details about. – Willem Van Onsem Dec 12 '21 at 16:44
  • @WillemVanOnsem In fact I tried to use the example of the documentation, for the CBV: https://docs.djangoproject.com/en/3.2/topics/testing/advanced/#testing-class-based-views But it doesn't exactly fit my case probably – stackvrflw_usr Dec 12 '21 at 17:07
  • @stackvrflw_user: but what is `self.profile_view_instance(self.user2)` doing? Why are you *calling* the view? Notice that a `CreateView` works different from a `DetailView`: in a `CreateView`, there is no object. – Willem Van Onsem Dec 12 '21 at 17:17
  • @WillemVanOnsem profile_view_instance is a function in class BaseTest(TestCase), to create an instance of the view, with RequestFactory. It's just refactoring to use it multiple times. – stackvrflw_usr Dec 12 '21 at 17:23
  • yes, but a `DetailView` implements logic in the `get` to first obtain the object, and set it as an `.object` attribute. That logic is, unfortunately, not encapsulated in the view, hence you will need to make calls and add boilerplate code to the test which is not advisable. Django has a tool to test views: the `Client`: it will automatically run the view, and will return a patched `HttpResponse` such that you can look to the context that was used to generate the response with `response = client.get('/profile/some-uuid')`, and `response.context['uuid']`. – Willem Van Onsem Dec 12 '21 at 17:28

1 Answers1

0

The profile_view_instance is not sufficient: a DetailView has some boilerplate logic in the .get(…) method that is necessary to run before the .get_context_data(…) will be triggered. It is also not a good idea to implement this logic in the test function, since then you duplicate logic, and it will be painful to update it each time you update the Django version.

Django has created a client that can be used to trigger a view. You can define a test with:

class ProfileViewTestCase(BaseTest):

    def test_get_context_data(self):
        self.client.force_login(self.user2)
        response = self.client.get(f'/profile/{self.user2.uuid}')
        context = response.context
        self.assertIn('uuid', context)
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555