当 Django 视图没有 return 值时如何测试它们

How do I test Django views when they don't return values

我是 Django 的新手。我已经学习了一些教程,编写了一些模型、视图、表单等,但我不明白如何测试它们,因为要么没有 returned,要么 returned 就是如此紧密结合它在测试方面没有意义。

例如这里有两个视图 类:

class ListBlogPostView(ListView):
    model = BlogPost
    template_name = 'app/blogpost_list.html'


class CreateBlogPostView(CreateView):
    model = BlogPost
    template_name = 'app/blogpost_edit.html'
    form_class = BlogPostForm

    def get_success_url(self):
        return reverse('blogpost-list')

    def get_context_data(self, **kwargs):
        context = super(CreateBlogPostView, self).get_context_data(**kwargs)
        context['action'] = reverse('blogpost-new')
        return context

第一个 ListBlogPostView 没有 return 任何东西。我怎样才能检查它是否正常工作?

第二个有几个函数,但它们的 return 值不是我可以用 assert

测试的东西

我怎么能对 Django 使用 TDD 方法?

我习惯在 Visual Studio 中使用 nunit 和 MS 'unit' 测试,模拟等

实际上,Django 的 generic class-based 视图没有理由不被称为 generic。

您可以查看两者的来源ListView and CreateView


ListView 有一个 GET 方法处理程序:

class BaseListView(MultipleObjectMixin, View):
    """A base view for displaying a list of objects."""
    def get(self, request, *args, **kwargs):
        self.object_list = self.get_queryset()
        allow_empty = self.get_allow_empty()

        if not allow_empty:
           # ...

        context = self.get_context_data()
        return self.render_to_response(context)

哪个 return 是一个有效的 Django 响应并且可以在 tests.py 中测试。


如果您查看 CreateView,它继承自 BaseCreateView(就像 ListView 继承自 BaseListView):

class BaseCreateView(ModelFormMixin, ProcessFormView):
    """
    Base view for creating an new object instance.
    Using this base class requires subclassing to provide a response mixin.
    """
    def get(self, request, *args, **kwargs):
        # ...

    def post(self, request, *args, **kwargs):
        # ...

也继承自ProcessFormView:

class ProcessFormView(View):
    """Render a form on GET and processes it on POST."""
    def get(self, request, *args, **kwargs):
        """Handle GET requests: instantiate a blank version of the form."""
        return self.render_to_response(self.get_context_data())

    def post(self, request, *args, **kwargs):
        """
        Handle POST requests: instantiate a form instance with the passed
        POST variables and then check if it's valid.
        """
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

GET 请求将产生有效响应。如您所见,此处的 POST 方法处理程序 return 是 self.form_valid(form)self.form_invalid(form),具体取决于表单状态。

这两个方法的源码见ViewMixin:

def form_valid(self, form):
    """If the form is valid, redirect to the supplied URL."""
    return HttpResponseRedirect(self.get_success_url())

def form_invalid(self, form):
    """If the form is invalid, render the invalid form."""
    return self.render_to_response(self.get_context_data(form=form))

这两种方法 return 一个有效的 可测试 Django 响应。


总之,你的ListBlogPostViewCreateBlogPostView都可以直接在tests.py中测试。您只需要更详细地查看 the implementation of Django's generic views开源的力量!

测试 view 与函数不同,大多数时候不会有 return 值。我这样做的方法是断言 html 响应。

所以对于 ListBlogPostView 这取决于 blogpost_list.html 模板中的内容。

一般视图测试应如下所示:

class ListBlogPostViewTest(TestCase):

    def test_blogpost_list_view(self): 
        response = self.client.get(reverse('blogpost-list'))
        html = response.content.decode('utf8')  
        self.assertTrue(html.startswith('<html>'))
        self.assertIn('<title>BlogPost lists</title>', html)  
        self.assertTrue(html.endswith('</html>'))

对于具有 contextview,您实际上可以检查它是否正在被检索并正确传递给查看。

blogPost = BlogPost.object.get(id=1)
self.assertEqual(response.context['blogPost'].name, blogPost.name)

How could I ever use a TDD approach to Django?

至于 TDD,您只需在创建视图之前先测试 html 视图即可。这实际上取决于您喜欢测试的细节并找到平衡点。我更喜欢主要测试正在设置的 context 并且重要的 html 元素在 view.

你仍然可以肯定地测试很多参数-

  • 获取和 post 请求的状态代码
  • 上下文数据(例如表单)中的变量
  • 使用断言模板
  • 在创建视图
  • 的情况下根据 post 请求创建对象
  • 使用状态代码检查权限

问题是对 Django 视图的测试在技术上是集成测试。只要您的测试足够细化,就意味着您不会在视图中测试表单或模型的代码,如果您遵循经典 TDD,我看不出有任何问题。