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

文章目录

  • 1. 前言
  • 2. 安装 Coverage
  • 3. 简单配置 Coverage
  • 4. 运行 Coverage
  • 5. 完善 Coverage 配置
  • 6. 生成 HTML 报告
  • 7. 完善单元测试

1. 前言

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

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

Coverage.py (以下简称 Coverage)是 Python 测试界最为流行的一个库之一,用来统计测试覆盖率。测试覆盖率可以从一个角度衡量代码的质量,覆盖率越高,说明测试越充分,代码出现 bug 的几率也就越小。当然需要注意的是,测试覆盖率仅仅只是衡量代码质量的一个角度,即使是 100% 的覆盖率也不能说代码就是完美的,没有 bug 的。

2. 安装 Coverage

要使用 Coverage,首先当然是安装它:

$ pipenv install coverage --dev

因为只在开发时才用得到,所以使用 Pipenv 安装时加 --dev 选项将其标记为开发时的依赖库。

3. 简单配置 Coverage

Coverage 支持很多配置选项,为了方便,通常将这些配置写在名为 .coveragerc 的文件中,Coverage 运行时会从项目根目录读取这个配置文件。因此先在项目根目录创建这个文件并写入最基本的配置:

[run]
branch = True
source = .
[report]
show_missing = True

Coverage 的配置遵循 ini 文件语法。简单来说就是,[section] 代表一个配置块,用于组织相关的一组配置。例如这里 [run] 是一个配置块,[report] 是另一个配置块,两个块下都有相关的一些配置项。配置项的格式为 key = value 。这几个简单配置项的含义为:

  1. branch = True 是否统计条件语句的分支覆盖情况。if 条件语句中的判断通常有 True 和 False 两种情况,设置 branch = True 后,Coverage 会测量这两种情况是否都被测试到。
  2. source =. 指定需统计的源代码目录,这里设置为当前目录(即项目根目录)。
  3. show_missing = True 在生成的统计报告中显示未被测试覆盖到的代码行号。

4. 运行 Coverage

简单配置后,我们就可以来运行 Coverage 了。打开命令行,进入项目根目录,依次运行下面的命令(注意如果没有激活python shell 虚拟需使用 pipenv run 让命令在虚拟环境中执行)。

首先运行 erase 命令清除上一次的统计信息

$ pipenv run coverage erase

manage.py test 运行 django 单元测试,这是这一次用 coverage run 来运行

$ pipenv run coverage run manage.py test

生成覆盖率统计报告

$ pipenv run coverage report

覆盖率统计报告输出如下:

Name                                             Stmts   Miss Branch BrPart  Cover   Missing
--------------------------------------------------------------------------------------------
_credentials.py                                      2      2      0      0     0%   1-2
blog\__init__.py                                     0      0      0      0   100%
blog\admin.py                                       11      0      0      0   100%
blog\apps.py                                         4      0      0      0   100%
blog\elasticsearch2_ik_backend.py                    8      0      0      0   100%
blog\feeds.py                                       12      0      0      0   100%
blog\migrations\0001_initial.py                      7      0      0      0   100%
blog\migrations\0002_auto_20190711_1802.py           7      0      0      0   100%
blog\migrations\0003_auto_20191011_2326.py           4      0      0      0   100%
blog\migrations\0004_post_views.py                   4      0      0      0   100%
blog\migrations\__init__.py                          0      0      0      0   100%
blog\models.py                                      62      0      0      0   100%
blog\search_indexes.py                               8      0      0      0   100%
blog\templatetags\__init__.py                        0      0      0      0   100%
blog\templatetags\blog_extras.py                    15      0      0      0   100%
blog\tests\__init__.py                               0      0      0      0   100%
blog\tests\test_models.py                           58      0      2      0   100%
blog\tests\test_smoke.py                             4      0      0      0   100%
blog\tests\test_templatetags.py                    115      0      2      0   100%
blog\tests\test_utils.py                            11      0      0      0   100%
blog\tests\test_views.py                           170      0      8      0   100%
blog\urls.py                                         4      0      0      0   100%
blog\utils.py                                       10      0      2      1    92%   14->16
blog\views.py                                       40      7      2      0    79%   64-72
blogproject\__init__.py                              0      0      0      0   100%
blogproject\settings\__init__.py                     0      0      0      0   100%
blogproject\settings\common.py                      22      0      0      0   100%
blogproject\settings\local.py                        5      0      0      0   100%
blogproject\settings\production.py                   5      5      0      0     0%   1-8
blogproject\urls.py                                  4      0      0      0   100%
blogproject\wsgi.py                                  4      4      0      0     0%   10-16
comments\__init__.py                                 0      0      0      0   100%
comments\admin.py                                    6      0      0      0   100%
comments\apps.py                                     4      0      0      0   100%
comments\forms.py                                    6      0      0      0   100%
comments\migrations\0001_initial.py                  7      0      0      0   100%
comments\migrations\0002_auto_20191011_2326.py       4      0      0      0   100%
comments\migrations\__init__.py                      0      0      0      0   100%
comments\models.py                                  15      0      0      0   100%
comments\templatetags\__init__.py                    0      0      0      0   100%
comments\templatetags\comments_extras.py            12      0      2      0   100%
comments\tests\__init__.py                           0      0      0      0   100%
comments\tests\base.py                              10      0      0      0   100%
comments\tests\test_models.py                        8      0      0      0   100%
comments\tests\test_templatetags.py                 57      0      6      0   100%
comments\tests\test_views.py                        34      0      4      0   100%
comments\urls.py                                     4      0      0      0   100%
comments\views.py                                   17      0      2      0   100%
fabfile.py                                          21     21      0      0     0%   1-43
manage.py                                           12      2      2      1    79%   11-12, 20->exit
scripts\__init__.py                                  0      0      0      0   100%
scripts\fake.py                                     63     63     14      0     0%   1-106
--------------------------------------------------------------------------------------------
TOTAL                                              876    104     46      2    87%

倒数第二列是被统计文件的测试覆盖率,倒数第一列是未被覆盖的代码行号。大部分文件测试覆盖率为 100%,说明我们的测试还是比较充分的。但从报告结果中我们发现这样几个问题:

  1. 有一些文件其实并不需要测试,或者并非项目的核心文件(例如使用fabiric部署项目的脚本 fabfile.py,django 的 migrations 文件等),这些文件应该从统计中排除。
  2. Coverage 默认显示全部文件的覆盖率统计结果,如果文件比较多的话就不好查找非 100% 覆盖率的文件。毕竟我们的目标是提高代码覆盖率,因此已达 100% 覆盖的代码文件我们不再关心。我们要做的是找到非 100% 覆盖率的文件,为其添加缺失的测试。

5. 完善 Coverage 配置

可以通过添加 Coverage 配置项轻松解决上面 2 个问题。

在 [run] 配置块中增加 omit 配置项可以指定排除统计的文件。在 [report] 配置块中增加 skip_covered 配置项可以指定统计报告中不显示 100% 覆盖的文件。

这是 .coveragerc 最终配置结果,注意我们在 omit 配置项中指定忽略了一些非核心的项目文件:

[run]
branch = True
source = .
omit =_credentials.pymanage.pyblogproject/settings/*fabfile.pyscripts/fake.py*/migrations/*blogproject\wsgi.py[report]
show_missing = True
skip_covered = True

再次按照上一节所说的方式运行 Coverage,最终报告结果如下:

Name            Stmts   Miss Branch BrPart  Cover   Missing
-----------------------------------------------------------
blog\utils.py      10      0      2      1    92%   14->16
blog\views.py      40      7      2      0    79%   64-72
-----------------------------------------------------------
TOTAL             709      7     30      1    99%33 files skipped due to complete coverage.

这个报告指出我们仍有 2 个文件没有达到 100% 的覆盖率,我们要做的就是为这两个文件中未测试的代码增加单元测试,让其达到 100% 测试覆盖率。

不过在动手写测试之前,我们要搞清楚哪些代码没被测到。命令行报告的最后一列指出了未被测试代码的行号,但是这样看着不是很直观。一种体验更好的方式是生成 HTML 报告,这样我们可以直接在 HTML 报告中查看到未被测试到的具体代码。

6. 生成 HTML 报告

coverage report 命令在命令行生成统计报告,而 coverage html 则可以生成 HTML 报告。

在上一节的基础上,运行如下命令:

$ pipenv run coverage html

运行完成后项目根目录会多出一个 htmlcov 的文件夹,里面就是测试覆盖率的 HTML 报告文件。用浏览器打开里面的 index.html 文件就可以查看报告结果了:
在这里插入图片描述
主页和命令行的结果是一样的,不过我们可以点击文件名,进入到对这个文件更加具体的统计报告页面,例如 blog\views.py 结果如下:
在这里插入图片描述
绿色部分代表已覆盖的代码,红色部分代表未覆盖的代码。查看文件我们发现,blog\views.py 中未被覆盖的代码原来是 Django 博客实现简单的全文搜索 中的代码,现在我们已经将搜索替换为 Django Haystack 全文检索 了,这段代码也就不需要了,可以直接删除。

7. 完善单元测试

blog\utils.py 的报告结果则表明我们在 Django Haystack 全文检索与关键词高亮 中自定义的搜索关键词高亮器有一个 if 分支条件未被测试到:
在这里插入图片描述
检查 blog/tests/test_utils.py 中的测试用例,我们发现只测试了比较短的标题不被截断,也就是

if len(text_block) < self.max_length:

判断条件为 True,缺失对判断条件为 False 的测试。所以我们来构造一个新的测试用例测试标题长度超过 max_length (默认值为 200)的情况时会被截断:

class HighlighterTestCase(TestCase):def test_highlight(self):# 省略已有代码 ...highlighter = Highlighter("标题")document = "这是一个长度超过 200 的标题,应该被截断。" + "HelloDjangoTutorial" * 200self.assertTrue(highlighter.highlight(document).startswith('...<span class="highlighted">标题</span>,应该被截断。'))

再次运行 Coverage 生成报告,测试覆盖率全都 100% 了!

$ pipenv run coverage erase
$ pipenv run coverage run manage.py test
$ pipenv run coverage report
# 输出
Name    Stmts   Miss Branch BrPart  Cover   Missing
---------------------------------------------------
---------------------------------------------------
TOTAL     704      0     28      0   100%

最后提醒一点,Coverage 运行后可能会在项目目录下生成一些文件,这些文件并不需要纳入版本管理,所以将其加入 .gitignore 文件中,防止被提交到代码库:

htmlcov/
.coverage
.coverage.*
coverage.xml
*.cover

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

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

相关文章

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…

day212223:线程、进程、协程

1、程序工作原理 进程的限制&#xff1a;每一个时刻只能有一个线程来工作。多进程的优点&#xff1a;同时利用多个cpu&#xff0c;能够同时进行多个操作。缺点&#xff1a;对内存消耗比较高当进程数多于cpu数量的时候会导致不能被调用&#xff0c;进程不是越多越好&#xff0c;…

php课程 8-28 php如何绘制生成显示图片

php课程 8-28 php如何绘制生成显示图片 一、总结 一句话总结&#xff1a;gd库轻松解决 1、php图片操作生成的图的两种去向是什么&#xff1f; 一种在页面直接输出&#xff0c;一种存进本地磁盘 2、php操作图片的库有哪些&#xff1f; PHP: Image Processing and Generation - M…

flavr—超级漂亮的jQuery扁平弹出对话框

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 插件描述&#xff1a;flavr是一个时尚的扁平弹出对话框为您的下一个网站。 flavr是响应设计布局&#xff0c;能够适应任何屏幕大小。 得…

经纬度之间的距离计算

来自谷歌地图的计算公式&#xff1a; 通过JAVA的Math类各种方法调用。实现上述公式 private static double EARTH_RADIUS 6378.137;// 单位千米/*** 角度弧度计算公式 rad:(). <br/>* * 360度2π πMath.PI* * x度 x*π/360 弧度* * author chiwei* param d* return* s…