如何为django视图编写单元测试?

时间:2012-08-09 14:02:48

标签: django unit-testing

我在理解如何为django设计单元测试时遇到了问题。

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

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

@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()失败并且没有重定向,对吗?

2 个答案:

答案 0 :(得分:98)

NB NB! 我在下面描述的并不是严格意义上的“单元测试”;为Django视图代码编写独立的单元测试几乎是不可能的。这更像是一次集成测试...

你认为你的观点有几条路径是正确的:

    匿名用户
  1. GETPOST(应重定向到登录页面)
  2. 登录用户
  3. GETPOST没有个人资料(应提出UserProfile.DoesNotExist例外)
  4. 登录用户
  5. GET(应显示表单)
  6. 登录用户
  7. POST,空白数据(应显示表单错误)
  8. 登录用户使用无效数据
  9. POST(应显示表单错误)
  10. 登录用户使用有效数据
  11. POST(应重定向)
  12. 测试 1 实际上只是测试@login_required,因此您可以跳过它。无论如何我倾向于测试它(以防我或其他人忘记使用该装饰器)。

    我不确定 2 中的失败案例(500错误页面)是您真正想要的。我会想出你想要发生的事情(也许是use get_or_create(),或者抓住DoesNotExist例外并以这种方式创建一个新的配置文件。)

    根据您的自定义验证量, 4 可能不需要进行测试。

    无论如何,鉴于上述所有情况,我会做类似的事情:

    from django.test import TestCase
    
    class TestCalls(TestCase):
        def test_call_view_denies_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_loads(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_fails_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_fails_invalid(self):
            # as above, but with invalid rather than blank data in dictionary
    
        def test_call_view_fails_invalid(self):
            # same again, but with valid data, then
            self.assertRedirects(response, '/contact/1/calls/')
    

    显然,这里的缺点是硬编码的URL。您可以在测试中use reverse()build requests using RequestFactory并将您的观点称为方法(而不是通过网址)。但是,使用后一种方法,您仍然需要使用硬编码值或reverse()来测试重定向目标。

    希望这有帮助。

答案 1 :(得分:5)

Django附带了一个测试客户端,可用于测试完整的请求/响应周期:The docs包含一个向给定URL发出get请求并声明状态代码以及模板上下文的示例。您还需要一个测试,它执行POST并按预期断言成功重定向。