Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ensuring all Django views return 200?

I have lots of simple Django views that look like this:

@team_leader_required
def view_all_teams(request):
    teams = Team.objects.all()

    template_vars = {'toolbar': 'teams',
                     'teams': teams}
    return render(request, "all_teams.html", template_vars)

I end up writing lots of unit tests of the form:

def test_view_all_teams_renders(self):
    user = self.create_team_leader()
    self.log_in(user)

    response = self.client.get(reverse('all_teams'))
    self.assertHttp200(response)

Despite my convenience methods for creating users (e.g. .create_team_leader) and various convenience assertions (e.g. .assertHttp200), I still end up with a lot of duplication in my tests.

(My tests are simple since I can't see anything else useful to assert about these views -- TestCase.assertTemplateUsed breaks if you rename templates, even if the view is correct.)

It's easy to miss a test, which gives me little confidence when I rename templates. Is there any way I can automatically generate test cases? Something like (psuedo-code):

for every view in urls:
    if view doesn't take extra arguments:
        test that view returns 200 when a logged in superuser does a GET

EDIT

Here's a representative snippet from my urls.py:

urlpatterns = patterns('',
    url(r'^teams/$', 'teams.views.view_all_teams', name='all_teams'),
    url(r'^teams/major/$', 'teams.views.view_major_teams', name='major_teams'),
    url(r'^teams/minor/$', 'teams.views.view_minor_teams', name='minor_teams'),
    url(r'^teams/(?P<team_id>\d+)/$', 'teams.views.view_team', name='view_team'),
    url(r'^teams/(?P<team_id>\d+)/edit$', 'teams.views.edit_team', name='edit_team'),
    url(r'^teams/(?P<team_id>\d+)/delete$', 'teams.views.delete_team', name='delete_team'),

I'd like to automatically test the first three views in this list.

like image 952
Wilfred Hughes Avatar asked Jun 19 '13 14:06

Wilfred Hughes


1 Answers

from django.core import urlresolvers
from django.test import TestCase


class SimpleTest(TestCase):
    def test_simple_views(self):
        url_names = [
            'all_teams',
            'major_teams',
            'minor_teams',
            'view_team',
            'edit_team',
        ]

        user = self.create_team_leader()
        self.log_in(user)

        for url_name in url_names:
            try:
                url = urlresolvers.reverse(url_name, args=(), kwargs={})
            except urlresolvers.NoReverseMatch:
                #print('Pass {}'.format(url_name))
                continue

            #print('Try {}'.format(url_name))
            response = self.client.get(url)
            self.assertHttp200(response)

If all url patterns have their name, You can use follow code to define url_names:

url_names = [p.name for p in teams.urls.urlpatterns]

Known Issues

  • If view function fail, you will not know which view failed.
  • Views next to failed view will not be tested.

Another version that handle above issues.

import unittest

from django.core import urlresolvers
from django.test import TestCase

from teams.urls import urlpatterns


class SimpleTest(TestCase):
    ...

    def setUp(self):
        user = self.create_team_leader()
        self.log_in(user)

    url_names = [p.name for p in urlpatterns]
    vs = vars()
    def make_test_function(idx, url_name, url):
        def  t(self):
            response = self.client.get(url)
            self.assertHttp200(response)
        t.__name__ = 'test_' + idx
        t.__doc__ = 'simple get test for ' + url_name
        return t

    for i, url_name in enumerate(url_names):
        i = str(i)
        try:
            url = urlresolvers.reverse(url_name, args=(), kwargs={})
            vs['test_' + i] = make_test_function(i, url_name, url)
        except urlresolvers.NoReverseMatch as e:
            vs['test_' + i] = unittest.skip(url_name + ' requires parameter(s) or view not found')(lambda: 0)

    del url_names, vs, make_test_function,
like image 165
falsetru Avatar answered Oct 06 '22 19:10

falsetru