与 coveralls 的不解之缘

2024-03-09 04:59
文章标签 不解之缘 coveralls

本文主要是介绍与 coveralls 的不解之缘,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

阅读大约需要 4.5 分钟

前两天在 GitHub 浏览 Python 的三方库时,看到了以下图片

就像 https 那个绿色锁的标志一样,看着很可信,让人用着放心,很多开源项目都有这些图标。

看到 coverage 是 98%,我产生了疑问,这是手工统计的,还是程序自动测试出来的呢?

如果是手工统计的,肯定都往高了写,这样的数据也就没有价值,如果是程序自动测试出来的,想着都觉得复杂,是怎么实现的呢?带着这些疑问,我点击了那个 coverage 98%,跳转到了 https://coveralls.io/ 的页面。

探索了一番,发现原来这是叫 coveralls 的三方库实现的,用于在线实时显示单元测试的覆盖率,测试数据是通过 coverage 来跑出来的。

好奇的我 pip install 安装了下,拿自己之前的程序,写了几个单元测试,用了下这两条命令:

coverage run --source=dbinterface -m pytest tests/
coverage report -m

发现,这个单元测试的覆盖率果然是程序自动统计出来的,coverage 真的太牛了,有了这个,写单元测试就无法偷懒了,代码质量就有了量化标准。

从上面的图中可以看到文件的哪些代码行没有测试到,然后针对性的编写单元测试。还可以生成 html 文件进行查询,更为直观。

猜测 coverage 应该是记录了 pytest 调用的代码行数,然后从全部代码行记录中去除已经测试过的行记录,就是未测试的代码行,从而统计覆盖率。

当时,我不由自主发出了‘卧槽牛批’,不过仍然有疑问,程序是怎么检测哪些代码行被执行了呢?虽然我知道 debug 时可以看到,但是如何写程序统计,我还一无所知。

好奇心驱使着我去探索。

首先看下这个 coverage 来自哪里,里面有什么内容:

(py38env) ➜  dbinterface git:(master) ✗ which coverage
/Users/aaron/py38env/bin/coverage

可以看到 coverage 的内容:


(py38env) ➜  dbinterface git:(master) ✗ cat /Users/aaron/py38env/bin/coverage
#!/Users/aaron/py38env/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from coverage.cmdline import main
if __name__ == '__main__':sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])sys.exit(main())
(py38env) ➜  dbinterface git:(master) ✗

其实,在命令行执行 coverage,就相当于执行:

/Users/aaron/py38env/bin/python3 coverage

将该文件保存到一个目录中,命名为 main.py,然后使用 PyCharm IDE 开始调试,调试的过程中,发现 coverage run --source=dbinterface -m pytest tests/ 命令会将测试的结果写入到文件 .coverage 中,再执行 coverage report -m 时会从该文件统计出覆盖率。

也就是说关键是要弄清楚命令 coverage run --source=dbinterface -m pytest tests/ 的执行过程。

继续 Debug,这里说下,由于我们的命令是在路径 /Users/aaron/github/somenzz/dbinterface 下执行的,在 Debug 前,先使用 os.chdir 改变程序的工作目录:

main.py


#!/Users/aaron/py38env/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from coverage.cmdline import main
import os
if __name__ == '__main__':os.chdir('/Users/aaron/github/somenzz/dbinterface')sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])print(sys.argv[0])sys.exit(main())

然后加入参数,打好断点,追踪。

最后追踪到这里:

这里的 tracer 类就是 CTracer,其源头可以从 collector.py 文件的这段代码看出来:

可以看出 tracer 的原型要么是 CTracer, 要么是 PyTracer。

从作者的注释中可以看到 CTracer 速度非常快,而 PyTracer 相对较慢。

想看 CTracer 的源代码,结果发现了这个文件

.so 文件相当于 windows 的 dll 文件,是动态链接库,需要反编译成汇编语言,然后再分析执行逻辑,这个对我来说太难了,自己又不熟悉汇编,于是放弃。

那就只剩下 PyTracer 了,原理应该是类似的,PyTracer 的源代码就是 pytracer.py 文件,可以直接打开查看。

文件开始的地方,导入了以下三个库:


import atexit
import dis
import sysfrom coverage import env

其中前三个都是标准库,atexit 是退出处理器,可以注册一个函数,在解释器终止时执行。dis 是 Python 字节码反汇编器,这两个的使用只有一次,没有派上大用处,可以忽略。

重点就是第三个 sys 模块,这个模块和 os 模块可以说是博大精深,很多程序都会使用到,从包的名称也可以总结规律,名字越短,就越重要,其使用频率就越高。

看 PyTracer 源代码, sys.settrace 是起决定作用的,是 coverage 能够统计单元测试覆盖率的关键。

下面是对 Python 官方文档对 sys.settrace 的介绍:

sys.settrace(tracefunc) 用来设置系统的跟踪函数,使得用户在 Python 中就可以实现 Python 源代码调试器。该函数是特定于单个线程的,所以要让调试器支持多线程,必须为正在调试的每个线程都用 settrace() 注册一个跟踪函数,或使用 threading.settrace()。

跟踪函数应接收三个参数:frame、event 和 arg。frame 是当前的堆栈帧。event 是一个字符串:'call'、'line'、'return'、'exception' 或 'opcode'。arg 取决于事件类型。

官方文档 bb 这么多,说实话我也没太懂,到底咋用呢?我网上找了一个例子,比如说文件 trace.py 内容如下:

import sysdef stuff():print("calling stuff")def printer(frame, event, arg):print(frame, event, arg)return printer # return itself to keep tracingsys.settrace(printer)
stuff()

也就是说执行函数之前,加上 sys.settrace。执行该文件,可以得到以下结果:

(py38env) ➜  tmp python trace.py
<frame at 0x7fa6bff5a440, file 'trace.py', line 3, code stuff> call None
<frame at 0x7fa6bff5a440, file 'trace.py', line 4, code stuff> line None
calling stuff
<frame at 0x7fa6bff5a440, file 'trace.py', line 4, code stuff> return None
(py38env) ➜  tmp

程序执行的行数,执行的操作都完整的显示了出来,将这些数据保存到文件中,就可以进行单元测试覆盖率的统计了。

虽然无法方便的查询 CTracer 源码,但是从 PyTracer 这里还是学习到了 coverage 统计单元测试覆盖率的统计方法。

一次偶遇 coveralls 让我见识了 Python 原来还可以统计代码的执行情况,真的太秀了。

趁热打铁,我用 coveralls 的状态图标也发布了一个工具库:dbinterface,单元测试覆盖率 89%:

这个一个数据库操作的通用接口,使用起来是相当的简单,从此读写各种数据库都不是事:

from dbinterface.database_client import DataBaseClientFactoryclient1 = DataBaseClientFactory.create(dbtype="postgres",host="localhost",port=5432,user="postgres",pwd="121113",database="postgres",)client2 = DataBaseClientFactory.create(dbtype="mysql",host="localhost",port=3306,user="aaron",pwd="aaron",database="information_schema",)result1 = client1.read("select current_date")
rows_affeted = client1.write("insert into tmp_test_table values(%s, %s)", ("1", "aaron")
)
rows_export = client.export("select * from information_schema.TABLES",params=(),file_path="/Users/aaron/tmp/mysql_tables.txt",delimeter="0x02",quote="0x03",all_col_as_str=False,)assert rows_export > 0

项目地址:https://github.com/somenzz/dbinterface

好奇心是学习技术的原动力,关注公众号「Python七号」,每周分享一个 Python 小技术。

留言讨论

这篇关于与 coveralls 的不解之缘的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/789585

相关文章

【C/C++内存管理】——我与C++的不解之缘(六)

前言         最近开学了,更新有些迟缓了; 现在来学C/C++中的内存管理 一、C/C++内存分布         在之前C一样学习过程中,学到过一些内存分布;现在先来看以下代码: int globalVar = 1;static int staticGlobalVar = 1;void Test(){static int staticVar = 1;int loc

马斯克被告“狗狗币传销”!索赔2580亿美元,法官驳回诉讼!马斯克与狗狗币的不解之缘!

在数字货币领域,每一次波动都牵动着全球投资者的神经,而埃隆·马斯克(Elon Musk)——这位科技界的传奇人物,更是以其独特的言行不断在加密货币市场上掀起波澜。近期,关于马斯克与狗狗币(Dogecoin)的一场法律纠纷,最终以马斯克的胜利告终,这一事件引起了广泛关注和讨论。 狗狗币,这一最初作为玩笑而诞生的加密货币,在马斯克的力推下迅速走红。马斯克频繁在社交媒体上提及狗狗币,甚至将

coveralls使用pytest进行本地测试时报错SyntaxError: invalid escape sequence \S

## 错误复现: git clone git@github.com:TheKevJames/coveralls-python.gitcd coveralls-pythonpoetry installpoetry run pytest ## 错误内容: ## 完整的打印信息 =====================================================

微信小程序开发秘籍:揭秘基础库版本与客户端版本的不解之缘【代码示例】

微信小程序开发秘籍:揭秘基础库版本与客户端版本的不解之缘【代码示例】 基础概念:何为基础库?何为客户端?基础库(Weixin Mini Program Base Library)客户端(WeChat Client) 版本关系:如何共舞?版本兼容性策略 实战演练:版本检测与动态适配示例:条件渲染新特性 结语与探讨 在微信小程序的开发旅程中,理解基础库版本与客户端版本之间的关系是每位

Coveralls自动测试代码覆盖率

Coveralls自动测试代码覆盖率 一.概要 借助Travis CI或者Jenkins等持续集成服务,向用户报告自动测试的测试覆盖率. 二.条件 代码托管在GitHub.已经集成了Travis CI或者Jenkins等服务. 三.集成 .I 注册登录Coveralls: 访问https://coveralls.io/sign-in 由GitHub账户登录 .II 添加对象仓库

我与CSDN的不解之缘

在我写第一篇博客的时候,我还得写的是C语言笔记,那时候我才刚上大一,刚刚接触编程、计算机、代码等等一些比较涉及内核的东西,我发现自己之前对于电脑的认识还仅仅停留在表明,我也深刻意识到了自己的不足。    在这里我学会了很多东西,列如: c语言编程: 数据可视化页面: 数据分析: 等等一系列知识。 我打开了一个新世界的大门 在我的不懈努力之下,我有幸入围了博客之星的评选

和程序员有些不解之缘

没来由的想起九年前的六月七,不管是谁或许都不会想到一个青涩俏皮的丫头会变成铁铮铮的“汉子”。        高考那年我没发现自己有些许紧张,在那之前我几乎没带着脑袋活着(我是那么想的),觉着自己不在乎,其实在乎反而适得其反吧,他们都说大学,那会的大学在我心里和高中、初中学校没啥差别,没有想过上什么大学,几乎不知道何为大学,只听旁人聊起重点大学,对我来说当然是遥不可以及的,我更加坦然

String与BigDecimal的不解之缘

昨天在画界面的时候发现一个金额字段如果为空,显示出来就是一个“-”,样式很难看,所以决定在代码里给一个“¥0.00”这样子的值在界面上显示。 注意:只是显示在界面上的值,不修改数据库,只是用来看的,字段本身不可修改。 然后看了一下这个字段的元素类型,发现是个文本。 俺表示不理解,单这个字段的值来源于一个公共插件,我又不好去修改这个插件,万一崩了岂不是完犊子,最后想着把这个string转一下b

编织魔法——我与计算机的不解之缘

一、为什么当初选择计算机行业         小时候,我就对电脑充满了好奇。每次看到屏幕上闪烁的光标,我都觉得那是一种神秘的召唤。当我第一次听说“程序员”这个词,我就知道那是我梦寐以求的职业。因为,我梦想成为神奇的码农,我想像编织魔法一样编写程序,创造出炫酷的虚拟世界。我觉得编程的过程就像是在用代码书写一段段奇妙的冒险故事,让每一个字符、每一行代码都充满生命力和想象力。通过编程,我可以实现任

JavaScript与多线程的不解之缘!

前言 对于前端开发者来说,多线程是一个比较陌生的话题。因为JavaScript是单线程语言。也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。 UI渲染与JavaScript是共同使用主线程。如果JavaScript运行过长,可能就会中断UI渲染,从而导致页面卡顿。 为此,JavaScript推出了异步的处理方法。但终归到底还是单线程的。而且随着