作者:HelloGitHub-追梦人物

文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库

评论应用的测试和博客应用测试的套路是一样的。

先来建立测试文件的目录结构。首先在 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 的测试。现在,我们想知道的是,到底我们的测试效果怎么样呢?测试充分吗?测试全面吗?还有没有没有测到的地方呢?

单凭肉眼观察难以回答上面的问题,接下来我们就借助一个工具,从代码覆盖率的角度来检测一下我们的测试效果究竟如何。

HelloDjango 往期回顾:

第 29 篇:编写 Django 应用单元测试

第 28 篇:Django Haystack 全文检索与关键词高亮

第 27 篇:开启 Django 博客实现简单的全文搜索


关注公众号加入交流群

Django 博客单元测试:测试评论应用的更多相关文章

  1. python 全栈开发,Day83(博客系统子评论,后台管理,富文本编辑器kindeditor,bs4模块)

    一.子评论 必须点击回复,才是子评论!否则是根评论点击回复之后,定位到输入框,同时加入@评论者的用户名 定位输入框 focus focus:获取对象焦点触发事件 先做样式.点击回复之后,定位到输入框, ...

  2. Django 博客开发教程目录索引

    Django 博客开发教程目录索引 本项目适合 0 基础的 Django 开发新人. 项目演示地址:Black & White,代码 GitHub 仓库地址:zmrenwu/django-bl ...

  3. Django1.8教程——从零开始搭建一个完整django博客(一)

    第一个Django项目将是一个完整的博客网站.它和我们博客园使用的博客别无二致,一样有分类.标签.归档.查询等功能.如果你对Django感兴趣的话,这是一个绝好的机会.该教程将和你一起,从零开始,搭建 ...

  4. 使用 Nginx 和 Gunicorn 部署 Django 博客(转)

    原文:http://zmrenwu.com/post/20/  http://www.siar.me/post/9/ 针对很多朋友反映按照教程的做法始终只能看到 Nginx 欢迎页面的问题,Tian ...

  5. Django博客开发实践,初学者开发经验

    python,Django初学者,开发简易博客,做了一下笔记,记录了开发的过程,功力浅薄,仅供初学者互相 交流,欢迎意见建议.具体链接:Django博客开发实践(一)--分析需求并创建项目 地址:ht ...

  6. django博客项目8:文章详情页

    首页展示的是所有文章的列表,当用户看到感兴趣的文章时,他点击文章的标题或者继续阅读的按钮,应该跳转到文章的详情页面来阅读文章的详细内容.现在让我们来开发博客的详情页面,有了前面的基础,开发流程都是一样 ...

  7. django博客项目6:Django Admin 后台发布文章

    在此之前我们完成了 Django 博客首页视图的编写,我们希望首页展示发布的博客文章列表,但是它却抱怨:暂时还没有发布的文章!如它所言,我们确实还没有发布任何文章,本节我们将使用 Django 自带的 ...

  8. django博客项目5:博客首页视图(2)

    真正的 Django 博客首页视图 在此之前我们已经编写了 Blog 的首页视图,并且配置了 URL 和模板,让 Django 能够正确地处理 HTTP 请求并返回合适的 HTTP 响应.不过我们仅仅 ...

  9. django博客项目3:创建 Django 博客的数据库模型

    设计博客的数据库表结构 博客最主要的功能就是展示我们写的文章,它需要从某个地方获取博客文章数据才能把文章展示出来,通常来说这个地方就是数据库.我们把写好的文章永久地保存在数据库里,当用户访问我们的博客 ...

随机推荐

  1. [bzoj3938] [Uoj #88] Robot

    Description 小 \(q\) 有 \(n\) 只机器人,一开始他把机器人放在了一条数轴上,第 \(i\) 只机器人在 \(a_i\) 的位置上静止,而自己站在原点.在这之后小 \(q\) 会 ...

  2. springboot中使用logback

    原文地址:https://blog.csdn.net/tianyaleixiaowu/article/details/73321610 Springboot默认集成的就是logback,logback ...

  3. 软件发布!DOTA2统计学

    更新日志: 1.2 增加DOTABUFF作为数据源,可以与DOTAMAX切换 1.1 增加关于对话框 增加版本信息 修复列表框头的错误   受到 http://tieba.baidu.com/p/38 ...

  4. SpringBoot配置国际化

    1).国际化 1).编写国际化配置文件: 2).使用ResourceBundleMessageSource管理国际化资源文件 3).在页面使用fmt:message取出国际化内容 步骤: 1).编写国 ...

  5. [总结]ACM模拟总结

    1.心态一定要稳,千万不要慌. 2.内部交流要多点,说不定就讨论出有用的性质了. 3.题目细节一定要想清楚. 4.一道题绝对不能让多个人来写. 5.英语要好好学.

  6. Python3基础之正则表达式

    正则表达式 在线测试工具 http://tool.chinaz.com/regex/ 同一个位置上可以出现的字符的范围. 字符组 : [字符组] 在同一个位置可能出现的各种字符组成了一个字符组,在正则 ...

  7. CentOS7下部署2套Python版本共存

    参考地址:https://www.cnblogs.com/xuaijun/p/7985245.html 源码的安装一般由3个步骤组成:配置(configure).编译(make).安装(make in ...

  8. springboot中使用自定义注解实现策略模式,去除工厂模式的switch或ifelse,实现新增策略代码零修改

    前言 思路与模拟业务 源码地址 https://gitee.com/houzheng1216/springboot 整体思路就是通过注解在策略类上指定约定好的type,项目启动之后将所有有注解的typ ...

  9. Nutz | Nutz项目整合Spring实战

    Nutz项目整合Spring实战 前言 Github地址 背景 实现步骤 加入springMvc与Spring 相关配置 新增Spring相关配置 新增SpringIocProvider 重写Nutz ...

  10. ActiveMQ 快速入门教程系列 第二章 发布-订阅者模式实现

    第二章我们会介绍怎样实现一个发布者对多个订阅者的消息传递 Topic和queue的最大区别在于topic是以广播的形式,通知所有在线监听的客户端有新的消息,没有监听的客户端将收不到消息:而queue则 ...