How TDD can be applied to Django Class based Generic Views?
Asked Answered
D

3

24

Since Class based Generic Views in Django involve some work by the framework I find very hard to work with them in a TDD style. Now I use the TestClient to access the view from the http mocked stack, but I would prefer to properly unittest specific methods (es. overrides of get_object and get_queryset ) before 'functional' testing with the TestClient.

Is there a ( quick ) way to obtain a proper instance of a ClassView to perform unit test on it?

Defrost answered 22/12, 2011 at 11:12 Comment(1)
The question is hard to follow. Are you asking how to create model object instances in the setUp method of a test?Crustaceous
V
17

Generally, that would include creating a request via the RequestFactory and instantiating the view class with keyword arguments. Afterwards, you can call any of the view methods and evaluate the result, passing any required arguments.

I'd recommend that you review the base View class, specifically the __init__, as_view and dispatch methods. They're crucial to understanding how the framework interacts with view objects.

The most important bit to notice there is that view methods expect to be called during a request-response process, so they're allowed to rely upon self.request, self.args and self.kwargs to be present before they're called, so make sure you've got that covered.

Vandervelde answered 22/12, 2011 at 12:36 Comment(2)
This is a good "points to resources" answer, but it would be a looot more helpful with a quick example to get to the point of >>Afterwards, you can call any of the view methods and evaluate the result, passing any required argumentsTucana
response = MyClassBasedView.as_view()(request)Verbenia
G
8

Not sure if this is exactly what you're looking for, but this is an example of how I try to unit test my views (untested code below):

import unittest
from django.core.urlresolvers import reverse
from django.test.client import RequestFactory
from ..views import MyClassBasedView

class MyClassBasedViewTestCase(unittest.TestCase):

    def setUp(self):
        self.factory = RequestFactory()

    def test_list_view(self):
        request = self.factory.get(reverse('your_url'))
        # additional params can go after request
        response = MyClassBasedView.as_view()(request)
        self.assertEqual(response.status_code, 200)

I'd also recommend looking at the documentation that Filip mentioned in his answer.

Garlan answered 23/8, 2013 at 15:59 Comment(2)
What if 'your_url' needs a positional argument, like a slug or pk? Where do you place it in this test?Wanitawanneeickel
@Mazzy had already answered my question: https://mcmap.net/q/425656/-how-do-i-pass-a-pk-or-slug-to-a-detailview-using-requestfactory-in-djangoWanitawanneeickel
E
5

I was looking for a simple solution for the same issue today and found this really great blog-post by Benoît Bryon (thanks!).

He suggested the following function:

def setup_view(view, request, *args, **kwargs):
    """Mimic as_view() returned callable, but returns view instance.

    args and kwargs are the same you would pass to ``reverse()``

    """
    view.request = request
    view.args = args
    view.kwargs = kwargs
    return view

Example

I wanted to test the following CBV:

class CreateList(CreateView):
    model = Item
    form_class = NewListForm
    template_name = 'lists/home_page.html'

    def form_valid(self, form):
        list_ = form.save(owner=self.request.user)
        return redirect(list_)

The necessary tests are for the form.save method arguments and for the redirect arguments, which should be the return value of the former. These tests will look something like:

    class CreateListTest(unittest.TestCase):

        def setUp(self):
            self.request = HttpRequest()
            self.request.user = Mock()
            self.form = Mock()
            self.view = setup_view(views.CreateList(), self.request)

        def test_form_dot_save_called_with_user(self):
            self.view.form_valid(self.form)
            self.form.save.assert_called_once_with(owner=self.request.user)

        @patch('lists.views.redirect')
        def test_redirect(self, mock_redirect):
            self.view.form_valid(self.form)
            mock_redirect.assert_called_once_with(self.form.save.return_value)
Eisteddfod answered 29/11, 2014 at 15:23 Comment(1)
Note that if you have *args or **kwargs you need to pass those through the setup_view function, per the comment in that function (different than if you use as_view()).Elwood

© 2022 - 2024 — McMap. All rights reserved.