IT教程 ·

Django 博客单元测试:测试批评运用

曹工说Spring Boot源码(20)-- 码网恢恢,疏而不漏,如何记录Spring RedisTemplate每次操作日志

 

批评运用的测试和博客运用测试的套路是一样的。

先来竖立测试文件的目次组织。起首在 comments 运用的目次下竖立一个名为 tests 的 Python 包,然后删除 comments 运用下 django 自动生成的 tests.py 文件,防备和 tests 包争执,再依据须要测试的内容,建立相应的 Python 模块。终究 tests 目次组织以下:

comments
    templatetags
    models.py
    ...
    tests
        __init__.py
        base.py
        test_models.py
        test_templatetags.py
        test_views.py

个中 base.py 用于寄存各个测试用例的大众的数据初始化基类。

数据基类

因为批评必需和文章关联,因而我们起首来写一个数据基类,用于初始化生成文章数据,别的测试类继续这个数据基类,从而不用在每一个测试类里都写一遍建立文章数据的代码了。

数据基类写在 base.py 模块里:

comments/tests/base.py

from django.apps import apps
from django.contrib.auth.models import User
from django.test import TestCase

from blog.models import Category, Post


class CommentDataTestCase(TestCase):
    def setUp(self):
        apps.get_app_config('haystack').signal_processor.teardown()
        self.user = User.objects.create_superuser(
            username='admin',
            email='admin@hellogithub.com',
            password='admin'
        )
        self.cate = Category.objects.create(name='测试')
        self.post = Post.objects.create(
            title='测试标题',
            body='测试内容',
            category=self.cate,
            author=self.user,
        )

要注重建立文章数据时,运用 apps.get_app_config('haystack').signal_processor.teardown() 断开建立索引的信号。

测试 Comment Model

Comment Model 的代码逻辑比较简朴,测试起来也很简朴:

comments/tests/test_models.py

from .base import CommentDataTestCase
from ..models import Comment


class CommentModelTestCase(CommentDataTestCase):
    def setUp(self):
        super().setUp()
        self.comment = Comment.objects.create(
            name='批评者',
            email='a@a.com',
            text='批评内容',
            post=self.post,
        )

    def test_str_representation(self):
        self.assertEqual(self.comment.__str__(), '批评者: 批评内容')

测试视图函数

我们只要一个宣布批评的视图函数,依据视图函数的逻辑,须要测试以下几点:

  1. 只处置惩罚 POST 要求,别的要求将返回 405 Method Not Allowed 毛病码。
  2. 假如批评的文章不存在,返回 404 毛病码。
  3. 假如提交的批评内容有毛病(比方 email 花样不正确),将衬着 preview.html 预览页面,而且预览页面显现批评失足的音讯提醒和批评表单中包括的毛病。
  4. 提交的内容正当,则建立批评,用户被重定向回被批评文章的详情页,页面中包括批评胜利的音讯提醒。

详细代码以下(省略掉了一些简朴的一看就懂的测试用例):

comments/tests/test_views.py

from django.urls import reverse

from .base import CommentDataTestCase
from ..models import Comment


class CommentViewTestCase(CommentDataTestCase):
    def setUp(self):
        super().setUp()
        self.url = reverse('comments:comment', kwargs={'post_pk': self.post.pk})
    
    # 省略掉了一看就懂的测试用例...

    def test_invalid_comment_data(self):
        invalid_data = {
            'email': 'invalid_email',
        }
        response = self.client.post(self.url, invalid_data)
        self.assertTemplateUsed(response, 'comments/preview.html')
        self.assertIn('post', response.context)
        self.assertIn('form', response.context)
        form = response.context['form']
        for field_name, errors in form.errors.items():
            for err in errors:
                self.assertContains(response, err)
        self.assertContains(response, '批评宣布失利!请修正表单中的毛病后从新提交。')

    def test_valid_comment_data(self):
        valid_data = {
            'name': '批评者',
            'email': 'a@a.com',
            'text': '批评内容',
        }
        response = self.client.post(self.url, valid_data, follow=True)
        self.assertRedirects(response, self.post.get_absolute_url())
        self.assertContains(response, '批评宣布胜利!')
        self.assertEqual(Comment.objects.count(), 1)
        comment = Comment.objects.first()
        self.assertEqual(comment.name, valid_data['name'])
        self.assertEqual(comment.text, valid_data['text'])

起首看到 test_invalid_comment_data 测试用例。这个测试用例中,我们组织了一个缺失批评内容、批评人名字且邮箱花样不正确的数据,然后将其提交了批评。接着就是对预期结果的断言。这里症结的一点是,衬着的预览页面应当包括提醒用户的表单毛病。所以我们从相应的上下文变量中获得表单 form 这个模板变量。接着运用以下代码猎取表单的毛病并断言相应中是不是包括了这些毛病:

for field_name, errors in form.errors.items():
    for err in errors:
        self.assertContains(response, err)

一旦表单绑定了数据,而且 is_valid 要领被挪用,就会有一个 errors 属性(参考批评视图函数中表单的处置惩罚逻辑)。errors 属性是一个类字典对象,假如表单数据不包括毛病,则为空;假如包括毛病数据,则其键为包括毛病数据的字段称号,值为该字段毛病提醒组成的列表(一个字段大概包括多个毛病,所以是一个列表)。比方这里的 form.errors,假如将其打印出来(运用 print(repr(form.errors))str 要领返回的内容是经衬着的 ul 列表),能够看到它的内容以下:

{'name': ['这个字段是必填项。'], 'email': ['输入一个有用的 Email 地点。'], 'text': ['这个字段是必填项。']}

test_valid_comment_data 中,我们组织正当的批评内容并提交,预期结果是批评提交胜利后重定向到被批评文章的详情页,所以运用了 assertRedirects 举行断言。

注重 self.client.post(self.url, valid_data, follow=True) 传入的 follow=True 参数。因为批评胜利后须要重定向,因而传入 follow=True,示意跟踪重定向,因而返回的相应,是终究重定向以后返回的相应(即被批评文章的详情页),假如传入 False,则不会追踪重定向,返回的相应就是一个相应码为 302 的重定向前相应。

关于重定向相应,运用 assertRedirects 举行断言,这个断言要领会对重定向的全部相应的历程举行检测,默许检测的是相应码从 302 变成 200。

测试模板标签

上一篇中引见过模板标签的测试要领。基础套路就是替代 django 视图函数自动衬着模板内容的历程,手工组织一个包括待测试模板标签的模板,然后手工衬着其内容,断言衬着后的内容是不是包括预期的内容。详细代码请看源代码,这里不再逐一解说,只将触及的几个新的表单操纵举行一个简朴引见。

class CommentExtraTestCase(CommentDataTestCase):
        # ...省略别的测试用例的代码
    
    def test_show_comment_form_with_invalid_bound_form(self):
        template = Template(
            '{% load comments_extras %}'
            '{% show_comment_form post form %}'
        )
        invalid_data = {
            'email': 'invalid_email',
        }
        form = CommentForm(data=invalid_data)
        self.assertFalse(form.is_valid())
        context = Context(show_comment_form(self.ctx, self.post, form=form))
        expected_html = template.render(context)
        
        for field in form:
            label = '<label for="{}">{}:</label>'.format(field.id_for_label, field.label)
            self.assertInHTML(label, expected_html)
            self.assertInHTML(str(field), expected_html)
            self.assertInHTML(str(field.errors), expected_html)

看到轮回表单 form 的语句:

for field in form:
    label = '<label for="{}">{}:</label>'.format(field.id_for_label, field.label)

我们这里运用了 field 的两个属性,id_for_labelid_for_label,分别是 django 表单自动生成的表单字段 label 的 id 和 label 名。别的就没什么好说的了,就是不停地断言页面包括预期的 HTML 内容。

至此,我们完成了对 blog 运用和 comment 运用这两个中心 app 的测试。如今,我们想知道的是,终究我们的测试结果怎样呢?测试充足吗?测试周全吗?另有没有没有测到的处所呢?

单凭肉眼视察难以回覆上面的问题,接下来我们就借助一个东西,从代码覆盖率的角度来检测一下我们的测试结果终究怎样。

 

如何用Java8 Stream API找到心仪的女朋友

参与评论