I have problems understanding how the unit tests should be designed for django.
From my understanding testing the whole view in one go seems impossible. We need to distinguish between pre-post and post states of request. But I have no idea how to design this. Is there any real life example?
Looking at the documentation the examples are too simplified and only focused on the model.
@login_required def call_view(request, contact_id): profile = request.user.get_profile() if request.POST: form = CallsForm(profile.company, request.POST) if form.is_valid() return HttpResponseRedirect('/contact/' + contact_id + '/calls/') else: form = CallsForm(profile.company, instance=call) variables = RequestContext(request, {'form':form} return render_to_response('conversation.html', variables)
update:
trying to make a success test work, but it still fails:
def test_contact_view_success(self): # same again, but with valid data, then self.client.login(username='username1', password='password1') response = self.client.post('/contact/add/', {u'last_name': [u'Johnson'], }) self.assertRedirects(response, '/')
error message:
AssertionError: Response didn't redirect as expected: Response code was 200 (expected 302)
I think this is because the form.is_valid() fails and it doesn't redirect, correct?
Writing tests Django's unit tests use a Python standard library module: unittest . This module defines tests using a class-based approach. When you run your tests, the default behavior of the test utility is to find all the test cases (that is, subclasses of unittest.
Django comes with a small set of its own tools for writing tests, notably a test client and four provided test case classes. These classes rely on Python's unittest module and TestCase base class. The Django test client can be used to act like a dummy web browser and check views.
The preferred way to write tests in Django is using the unittest module built-in to the Python standard library. This is covered in detail in the Writing and running tests document. You can also use any other Python test framework; Django provides an API and tools for that kind of integration.
NB NB! This isn't strictly a "unit test"; it's difficult and uncommon to write an independent unit test for Django view code. This is more of an integration test...
You're right that there are several pathways through your view:
GET
or POST
by anonymous user (should redirect to login page)GET
or POST
by logged-in user with no profile (should raise a UserProfile.DoesNotExist
exception)GET
by logged-in user (should show the form)POST
by logged-in user with blank data (should show form errors)POST
by logged-in user with invalid data (should show form errors)POST
by logged-in user with valid data (should redirect)Testing 1 is really just testing @login_required
, so you could skip it. I tend to test it anyway (just in case we've forgotten to use that decorator).
I'm not sure the failure case (a 500 error page) in 2 is what you really want. I would work out what you want to happen instead (perhaps use get_or_create()
, or catch the DoesNotExist
exception and create a new profile that way), or redirect to a page for the user to create a profile.
Depending on how much custom validation you have, 4 may not really need to be tested.
In any case, given all of the above, I would do something like:
from django.test import TestCase class TestCalls(TestCase): def test_call_view_deny_anonymous(self): response = self.client.get('/url/to/view', follow=True) self.assertRedirects(response, '/login/') response = self.client.post('/url/to/view', follow=True) self.assertRedirects(response, '/login/') def test_call_view_load(self): self.client.login(username='user', password='test') # defined in fixture or with factory in setUp() response = self.client.get('/url/to/view') self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'conversation.html') def test_call_view_fail_blank(self): self.client.login(username='user', password='test') response = self.client.post('/url/to/view', {}) # blank data dictionary self.assertFormError(response, 'form', 'some_field', 'This field is required.') # etc. ... def test_call_view_fail_invalid(self): # as above, but with invalid rather than blank data in dictionary def test_call_view_success_invalid(self): # same again, but with valid data, then self.assertRedirects(response, '/contact/1/calls/')
Obviously, a drawback here is hard-coded URLs. You could either use reverse()
in your tests or build requests using RequestFactory
and call your views as methods (rather than by URL). With the latter method, though, you still need to use hard-coded values or reverse()
to test redirect targets.
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With