71

我无法理解如何为 django 设计单元测试。

根据我的理解,一次测试整个视图似乎是不可能的。我们需要区分请求的 pre-post 和 post 状态。但我不知道如何设计这个。有没有现实生活中的例子?

查看文档,示例过于简化,仅关注模型。

@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)

更新:

试图使成功的测试工作,但它仍然失败:

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, '/')

错误信息:

AssertionError: Response didn't redirect as expected: Response code was 200 (expected 302)

我认为这是因为 form.is_valid() 失败并且它没有重定向,对吗?

4

2 回答 2

117

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:

  1. GET or POST by anonymous user (should redirect to login page)
  2. GET or POST by logged-in user with no profile (should raise a UserProfile.DoesNotExist exception)
  3. GET by logged-in user (should show the form)
  4. POST by logged-in user with blank data (should show form errors)
  5. POST by logged-in user with invalid data (should show form errors)
  6. 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.

于 2012-08-09T15:57:28.060 回答
9

Django ships with a test client which can be used to test the full request/response cycle: The docs contain an example of making a get request to a given url and asserting the status code as well as the template context. You would also need a test which does a POST and asserts a successful redirect as expected.

于 2012-08-09T15:25:38.207 回答