Skip to content

Instantly share code, notes, and snippets.

@timhughes
Created May 13, 2014 23:39
Show Gist options
  • Save timhughes/8e56c2955d3baa3fa2ca to your computer and use it in GitHub Desktop.
Save timhughes/8e56c2955d3baa3fa2ca to your computer and use it in GitHub Desktop.
from urllib.parse import urlparse
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import TestCase
class ViewTestMixin(object):
"""the name of the url to access the view. If you provide url, then it will take precedence."""
view_urlname = None
"""The path to the template for the view."""
view_template = None
"""The url to access the view. You can provide this or urlname. This takes precedence."""
view_url = None
def __init__(self, *args, **kwargs):
assert (self.view_urlname is not None) or (self.view_url is not None), "You must provide an urlname or an url."
if self.view_url is None and self.view_urlname is not None:
self.view_url = reverse(self.view_urlname)
super(ViewTestMixin, self).__init__(*args, **kwargs)
def test_view_loads(self):
self._login()
response = self.client.get(self.view_url)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, self.view_template)
def _login(self):
# See if LoginRequiredViewTestMixin is present, and if so, login
try:
self.login()
except AttributeError:
return
class FormViewTestMixin(object):
"""The name of the form in the context."""
form_context_name = None
"""A list of field names that _require_ data."""
form_required_fields = []
"""A mapping of field names to _invalid_ data for those fields. Optional since """
form_invalid_data = None
"""A mapping of field names to _valid_ data for those fields."""
form_valid_data = None
"""The name of the url that the view redirects to on succesful post."""
form_success_redirect_name = None
"""Instead of provding a form_success_redirect_name, you can provide the url path.
If you provide form_success_redirect_url, it takes precedence over form_success_redirect_name"""
form_success_redirect_url = None
def __init__(self, *args, **kwargs):
# Check that ViewTestMixin is present
assert getattr(self, 'view_url', False), "FormViewTestMixin requires ViewTestMixin"
# Check required fields
err_msg = ' must be provided'
assert self.form_context_name is not None, 'form_context_name' + err_msg
assert self.form_invalid_data is not None, 'form_invalid_data' + err_msg
assert self.form_valid_data is not None, 'form_valid data' + err_msg
# Make sure we have either a name or url for redirects
assert (self.form_success_redirect_name is not None) or \
(self.form_success_redirect_url is not None), \
"You must provide form_success_redirect_name or form_success_redirect_url."
if self.form_success_redirect_url is None and self.form_success_redirect_name is not None:
self.form_success_redirect_url = reverse(self.form_success_redirect_name)
super(FormViewTestMixin, self).__init__(*args, **kwargs)
def _login(self):
# See if LoginRequiredViewTestMixin is present, and if so, login
try:
self.login()
except AttributeError:
return
def test_view_fails_blank_post(self):
self._login()
response = self.client.post(self.view_url, {})
if self.form_context_name and self.form_required_fields:
for req_field in self.form_required_fields:
self.assertFormError(response, self.form_context_name, req_field, 'This field is required.')
def test_view_fails_invalid_post(self):
if self.form_context_name and self.form_invalid_data:
self._login()
response = self.client.post(self.view_url, )
for field_name, invalid_field in self.form_invalid_data.items():
self.assertFormError(response, self.form_context_name, field_name,
'This needs to be a different error message')
def test_view_succesful_post(self):
if self._success_url and self.form_valid_data:
self._login()
response = self.client.post(self.view_url, self.form_valid_data)
self.assertRedirects(response, self._success_url)
class LoginRequiredViewTestMixin(object):
"""The name of the url where login-required views will redirect to for anonymous users.
Typically this would be the view for logging in."""
login_urlname = None
"""The url path that the view redirects to for anonymous users. If both this and login_urlname
are provided, this takes precedence"""
login_url = None
def __init__(self, *args, **kwargs):
assert (self.login_url is not None) or (self.login_urlname is not None), \
"You must provide login_urlname or login_url"
if self.login_url is None and self.login_urlname is not None:
self.login_url = reverse(self.login_urlname)
super(LoginRequiredViewTestMixin, self).__init__(*args, **kwargs)
def assertRedirectsToLogin(self, response):
"""AFAIK, this just works with django-allauth as it specifically checks for
the `next` parameter on the login url."""
assert getattr(response, 'redirect_chain', False), "No redirecting done."
parsed = urlparse(response.redirect_chain[-1][0])
domainy = "{}://{}".format(parsed.scheme, parsed.netloc)
url = "{}{}?next={}".format(domainy, self.login_url, self.view_url)
self.assertRedirects(response, url)
def login(self):
pw = 'ja;sdoijfl'
username = 'fdsa09ujids'
self.user = get_user_model().objects.create(username=username, password='1234')
self.user.set_password(pw)
self.user.save()
login = self.client.login(username=username, password=pw)
self.assertTrue(login)
def test_view_denies_anonymous(self):
if self.login_urlname:
response = self.client.get(self.view_url, follow=True)
self.assertRedirectsToLogin(response)
response = self.client.post(self.view_url, follow=True)
self.assertRedirectsToLogin(response)
# Here's a test case for a view that requires a login
class DashViewTest(ViewTestMixin, LoginRequiredViewTestMixin, TestCase):
view_urlname = 'dashboard'
view_template = 'core/dashboard.html'
login_urlname = 'account_login'
# Here's a test for a view that has no forms and doesn't require a login
class HomeViewTest(ViewTestMixin, TestCase):
view_urlname = 'home'
view_template = 'general/index.html'
# Here's a test case for a view that has a form and requires a login
class PersonCreatePageTest(ViewTestMixin, LoginRequiredViewTestMixin, FormViewTestMixin, ORMTest):
view_urlname = 'person_create'
view_template = 'property/property_form.html'
login_urlname = 'account_login'
form_context_name = 'form'
form_required_fields = ['name', 'address1', 'city', 'state', 'zipcode']
form_invalid_data = {'name': 'a' * 300,
'address1': 'b' * 300,
'city': 'd' * 300,
'zipcode': '1',
'state': 'not a state here'}
form_valid_data = {'name': 'a name',
'address1': '123 Address',
'address2': 'Apt. 17',
'city': 'The City',
'zipcode': '12345-1234',
'state': 'CA'}
form_success_redirect_name = 'person_list'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment