Diango博客--24.单元测试:测试评论应用

文章目录

  • 1. 前言
  • 2. 数据基类
  • 3.测试 Comment Model
  • 4. 测试视图函数
  • 5. 测试模板标签

1. 前言

comments应用的测试和blog应用测试的套路是一样的。

先来建立测试文件的目录结构。首先在 comments 应用的目录下建立一个名为 tests 的 Python 包,然后删除 comments 应用下 django 自动生成的 tests.py 文件,防止和 tests 包冲突,再根据需要测试的内容,创建相应的 Python 模块。最终 tests 目录结构如下,其中 base.py 用于存放各个测试用例的公共的数据初始化基类。

在这里插入图片描述

2. 数据基类

由于评论必须和文章关联,因此我们首先来写一个数据基类,用于初始化生成文章数据,其它测试类继承这个数据基类,从而不用在每个测试类里都写一遍创建文章数据的代码了。数据基类写在 base.py 模块里:

文件位置:comments/tests/base.py


from django.apps import apps
from django.contrib.auth.models import User
from django.test import TestCasefrom blog.models import Category, Postclass 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() 断开创建索引的信号

3.测试 Comment Model

先回顾一下comments应用的models.py

from django.db import models
from django.utils import timezoneclass Comment(models.Model):name = models.CharField('名字', max_length=50)email = models.EmailField('邮箱')url = models.URLField('网址', blank=True)text = models.TextField('内容')created_time = models.DateTimeField('创建时间', default=timezone.now)post = models.ForeignKey('blog.Post', verbose_name='文章', on_delete=models.CASCADE)class Meta:verbose_name = '评论'verbose_name_plural = verbose_nameordering = ['-created_time']def __str__(self):return '{}: {}'.format(self.name, self.text[:20])

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

文件位置:comments/tests/test_models.py


from .base import CommentDataTestCase
from ..models import Commentclass 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__(), '评论者: 评论内容')

4. 测试视图函数

我们只有一个发表评论的视图函数,首先回顾一下:

from blog.models import Post
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_POSTfrom .forms import CommentForm
from django.contrib import messages@require_POST
def comment(request, post_pk):# 先获取被评论的文章,因为后面需要把评论和被评论的文章关联起来。# 这里我们使用了 Django 提供的一个快捷函数 get_object_or_404,# 这个函数的作用是当获取的文章(Post)存在时,则获取;否则返回 404 页面给用户。post = get_object_or_404(Post, pk=post_pk)# django 将用户提交的数据封装在 request.POST 中,这是一个类字典对象。# 我们利用这些数据构造了 CommentForm 的实例,这样就生成了一个绑定了用户提交数据的表单。form = CommentForm(request.POST)# 当调用 form.is_valid() 方法时,Django 自动帮我们检查表单的数据是否符合格式要求。if form.is_valid():# 检查到数据是合法的,调用表单的 save 方法保存数据到数据库,# commit=False 的作用是仅仅利用表单的数据生成 Comment 模型类的实例,但还不保存评论数据到数据库。comment = form.save(commit=False)# 将评论和被评论的文章关联起来。comment.post = post# 最终将评论数据保存进数据库,调用模型实例的 save 方法comment.save()messages.add_message(request, messages.SUCCESS, '评论发表成功!', extra_tags='success')# 重定向到 post 的详情页,实际上当 redirect 函数接收一个模型的实例时,它会调用这个模型实例的 get_absolute_url 方法,# 然后重定向到 get_absolute_url 方法返回的 URL。return redirect(post)# 检查到数据不合法,我们渲染一个预览页面,用于展示表单的错误。# 注意这里被评论的文章 post 也传给了模板,因为我们需要根据 post 来生成表单的提交地址。context = {'post': post,'form': form,}messages.add_message(request, messages.ERROR, '评论发表失败!请修改表单中的错误后重新提交。', extra_tags='danger')return render(request, 'comments/preview.html', context=context)

根据视图函数的逻辑,需要测试以下几点:

  1. 只处理 POST 请求,其它请求将返回 405 Method Not Allowed 错误码。
  2. 如果评论的文章不存在,返回 404 错误码。
  3. 如果提交的评论内容有错误(例如 email 格式不正确),将渲染 preview.html 预览页面,并且预览页面显示评论出错的消息提醒和评论表单中包含的错误。
  4. 提交的内容合法,则创建评论,用户被重定向回被评论文章的详情页,页面中包含评论成功的消息提醒。

具体代码如下:

文件位置:comments/tests/test_views.py

from django.apps import apps
from django.contrib.auth.models import User
from django.urls import reversefrom blog.models import Category, Postfrom ..models import Comment
from .base import CommentDataTestCaseclass CommentViewTestCase(CommentDataTestCase):def setUp(self) -> None:super().setUp()self.url = reverse("comments:comment", kwargs={"post_pk": self.post.pk})def test_comment_a_nonexistent_post(self):url = reverse("comments:comment", kwargs={"post_pk": 100})response = self.client.post(url, {})self.assertEqual(response.status_code, 404)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"])
  1. 在 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': ['这个字段是必填项。']}
  1. test_valid_comment_data 中,我们构造合法的评论内容并提交,预期结果是评论提交成功后重定向到被评论文章的详情页,所以使用了 assertRedirects 进行断言。
    注意 self.client.post(self.url, valid_data, follow=True) 传入的 follow=True 参数。由于评论成功后需要重定向,因此传入 follow=True,表示跟踪重定向,因此返回的响应,是最终重定向之后返回的响应(即被评论文章的详情页),如果传入 False,则不会追踪重定向,返回的响应就是一个响应码为 302 的重定向前响应。
    对于重定向响应,使用 assertRedirects 进行断言,这个断言方法会对重定向的整个响应的过程进行检测,默认检测的是响应码从 302 变为 200。

5. 测试模板标签

上一篇中介绍过模板标签的测试方法。基本套路就是代替 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_label 和 id_for_label,分别是 django 表单自动生成的表单字段 label 的 id 和 label 名。别的就没什么好说的了,就是不停地断言页面包含预期的 HTML 内容。

至此,我们完成了对 blog 应用和 comment 应用这两个核心 app 的测试。现在,我们想知道的是,到底我们的测试效果怎么样呢?测试充分吗?测试全面吗?还有没有没有测到的地方呢?

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/452111.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

结合shiro 的图形验证码生成

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 在做用户登录功能时&#xff0c;很多时候都需要验证码支持&#xff0c;验证码的目的是为了防止机器人模拟真实用户登录而恶意访问&#…

E24- please install the following Perl modules before executing ./mysql_install_db

2019独角兽企业重金招聘Python工程师标准>>> [roott-cet7 scripts]# ./mysql_install_db --basedir/usr/local/mysql/ --datadir/app/data/ --usermysql FATAL ERROR: please install the following Perl modules before executing ./mysql_install_db: Data::Dumpe…

SpringMVC异常报406 (Not Acceptable)的解决办法

使用SpsringMVC&#xff0c;使用restEasy调试&#xff0c;controller请求设置如下&#xff1a; Java代码 RequestMapping(value"/list",methodRequestMethod.GET,producesMediaType.APPLICATION_JSON_VALUE) ResponseBody public List<EditTimeout> list()…

Diango博客--25.使用Coverage统计测试覆盖率

文章目录1. 前言2. 安装 Coverage3. 简单配置 Coverage4. 运行 Coverage5. 完善 Coverage 配置6. 生成 HTML 报告7. 完善单元测试1. 前言 我们完成了对 blog 应用和 comment 应用这两个核心 app 的测试。现在我们想知道的是究竟测试效果怎么样呢&#xff1f;测试充分吗&#x…

Android动画之逐帧动画(FrameAnimation)详解

今天我们就来学习逐帧动画,废话少说直接上效果图如下: 帧动画的实现方式有两种&#xff1a; 一、在res/drawable文件夹下新建animation-list的XML实现帧动画 1、首先在res/drawable文件夹下添加img00-img24共25张图片 2、新建frame_anim.xml [html] view plaincopy <?xml v…

网络爬虫--1.通用爬虫和聚焦爬虫

文章目录一.前言二.通用爬虫1.工作原理2.通用爬虫的局限性三.聚焦爬虫一.前言 根据使用场景&#xff0c;网络爬虫可分为 通用爬虫 和 聚焦爬虫 两种。 其中通用网络爬虫是捜索引擎抓取系统&#xff08;Baidu、Google、Yahoo等&#xff09;的重要组成部分。主要目的是将互联网…

敏捷教练的工具箱

学习并不是简简单单的阅读和浏览&#xff0c;而是一个积累的过程&#xff0c;一个通过持续的学习&#xff0c;对自己的知识体系不断丰富、索引的过程。接下来我会从四个方面入手分享我的经验。 高质量的信息源和高效的学习 Google是一个很好的工具&#xff0c;通过它&#x…

python 发送邮件的两种方式【终极篇】

python 发送邮件的两种方式【终极篇】 一&#xff0c;利用python自带的库 smtplib简单高效 from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.header import Header import smtplib from django.conf import settingsmail_hos…

网络爬虫--2.HTTP和HTTPS

文章目录一.简介二.HTTP的请求与响应三.客户端HTTP请求1.格式2.请求方法四.常用的请求报头1.Host (主机和端口号)2.Connection (链接类型)3.Upgrade-Insecure-Requests (升级为HTTPS请求)4. User-Agent (浏览器名称)5. Accept (传输文件类型)6.Referer (页面跳转处)7.Accept-En…

IBM王阳:软件是凝聚创新力的最佳平台

导读&#xff1a;在IBM全球副总裁兼IBM中国开发中心总经理王阳博士看来&#xff0c;IBM百年不衰的根本原因在于将创新力凝结成软件然后进行合适的传播&#xff0c;其间最重要的是成功打造出了一个吸引人才、培养研发人才并激发出人才创新力的环境和氛围。而保持创新领导力的关键…

Jquery 多行拖拽图片排序 jq优化

<!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>jQuery图片拖动排序代码</title><style type"text/css">.item_container{position:relative;height:auto;overflow:hidden;} .item_content ul{li…

分享11款主流的开源编程工具

导读&#xff1a;有了开源编程工具&#xff0c;在基于开源许可证的情况下您可以轻松学习、修改、提高代码的质量&#xff0c;本文收集了11款最主流的且有价值的开源编程工具。或许会给您带来一丝惊喜。一起来看下吧。 NO.1 Rhomobile Rhodes Ruby或许是Github上第二大流行语言…

谁在告谁?移动专利混战图

移动领域激战正酣&#xff0c;同样是没有永远的朋友&#xff0c;只有永远的利益。 苹果刚刚起诉三星的Galaxy手机和平板电脑山寨了苹果的产品&#xff0c;而此前两家并没有过节。再比如微软和亚马逊以及HTC之间的授权协议争端。移动领域的争端如此之多&#xff0c;以至于看客无…

光棍节程序员闯关秀过关全攻略

maven/java/web/bootstrapQQ群&#xff1a;566862629。希望更多人一起帮助我学习。 光棍节程序员闯关秀过关全攻略。程序员的寂寞谁能懂?"SF光棍节程序员闯关秀"智力挑战小游戏火热上线&#xff0c;看看你能闯到第几关&#xff1f; 游戏地址: http://segmentfault…

jekins搭建

2019独角兽企业重金招聘Python工程师标准>>> 转自 https://www.cnblogs.com/hdwang/p/6081994.html &#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xff1d;&#xf…

块级元素的margin-left和margin-right的用法注意

此时是有效果显示的因为html文档流默认是从上往下&#xff0c;从左往右进行显示的&#xff0c;所以此时是有效果的。那如果此时把#son的块元素的margin-right:20px; 是没有效果的此时是没有效果的&#xff0c;如图所示&#xff1a;如果此时想要margin-right有效果的话&#xf…

Apache Tiles的基本使用

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1、概述 对于一个新的技术&#xff0c;了解其基本的概念和和原理是学好该技术的基础。 2、Tiles的概念 Tiles 是复合视图模式&#xff0…

网络爬虫--6.urllib库的基本使用(2)

文章目录一. urllib.parse.urlencode()和urllib.parse.unquote()二. Get方式三. 批量爬取百度贴吧数据四.POST方式五.关于CA六.处理HTTPS请求 SSL证书验证一. urllib.parse.urlencode()和urllib.parse.unquote() 编码工作使用urllib.parse的urlencode()函数&#xff0c;帮我们…

摩拜大数据杀熟?官方:老用户押金的确退款延迟

近日&#xff0c;有媒体曝出摩拜单车一些老用户出现押金难退现象。有的消费者点击退款后&#xff0c;系统不断奔溃&#xff1b;有的申请退款后&#xff0c;账户又莫名出现押金&#xff0c;就像未申请一样&#xff1b;也有人终于提交了退款&#xff0c;等候数日却迟迟不见到账。…

Junit Test使用样例

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 配置&#xff1a; 调用类&#xff1a; import java.util.List;import javax.annotation.Resource;import org.apache.shiro.crypto.Rand…