作为一家小公司,日常测试基本都是以手工测试为主。如何在频繁快速的迭代过程中有效保证手工测试覆盖的充分性,是一个不得不思考的问题。
覆盖率是度量测试完整性的一个手段,也是测试有效性的一个依据。当一个迭代版本完成手工测试后,如果可以通过分析本次迭代变更代码的覆盖率来评估前期的测试用例设计是否完善,开发提交的改动 QA 这边是否均了解全面,开发代码是否存在冗余。从而更好的进行补充测试,提升本次上线的信心。
PS:当然首先要说明的是变更代码覆盖率并不能绝对作为测试全面的一个保证,因为即便测试覆盖了代码,也可能受测试人员素质和能力的影响出现漏测。但是我们可以认为高覆盖率的代码不一定质量高,但是低覆盖率的代码质量一定不高。
通过 jenkins 的 jacoco 插件来创建代码覆盖率任务,这个网上流程有很多,这里就不多做介绍了。当然自己在部署中也遇到了一些坑,下面是我之前遇到的坑的总结,https://testerhome.com/topics/16925
在实现全量代码覆盖率后,发现作为一个庞大且迭代频繁的项目,全量的代码覆盖率意义并不大,因为我们要评估的每个迭代的测试情况,我们需要更精准的了解本次迭代的代码覆盖率情况,为了更好的让每个测试人员可以方便、高效的了解对应服务的覆盖率情况。从而开发了这个代码变更覆盖率平台。
变更代码覆盖率整体服务架构图
1.安装 mysql 数据库服务端 (推荐 5.7+),并设置为 utf-8 编码,创建相应 difftest 数据库,设置好相应用户名、密码,启动 mysql
2.修改:DiffTestPlatform/DiffTestPlatform/setting.py 里 DATABASE 的配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1', #数据库所在服务器的ip地址
'PORT': '3306', #监听端口 默认3306即可
'NAME': 'difftest', #新建数据库名
'USER': 'root', #数据库登录名
'PASSWORD': '123456', #数据库登录密码
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
},
}
}
3.命令行窗口执行 pip install -r requirements.txt 安装工程所依赖的库文件
4.命令行窗口切换到根目录 生成数据库迁移脚本,并生成表结构
python manage.py makemigrations CodeDiff #生成数据迁移脚本
python manage.py migrate #应用到db生成数据表
5.创建超级用户,用户后台管理数据库,并按提示输入相应用户名,密码,邮箱。
python manage.py createsuperuser
6.启动服务
python manage.py runserver 0.0.0.0:8000 #本地环境启动可以配置0.0.0.0,如果是正式环境需要配置访问ip
具体平台说明请见 github
File "/Users/mac/Desktop/DiffTestPlats/DiffTestPlatform/urls.py", line 18, in
from CodeDiff import views
File "/Users/mac/Desktop/DiffTestPlats/CodeDiff/views.py", line 5, in
from CodeDiff.util.run_diff import RunDiff
File "/Users/mac/Desktop/DiffTestPlats/CodeDiff/util/run_diff.py", line 6, in
from git import Repo
ModuleNotFoundError: No module named 'git'
(venv) macdeMacBook-Pro-6:DiffTestPlats mac$
各种报错,大佬方便回复下吧
大佬,你的运行环境的工具版本都是多少呀,现在执行一步就错一步
ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/user_guide/#fixing-conflicting-dependencies
(venv) macdeMacBook-Pro-6:DiffTestPlatform mac$ pip3 install mysqlclient==1.4.2.post1
Collecting mysqlclient==1.4.2.post1
Using cached mysqlclient-1.4.2.post1.tar.gz (85 kB)
Preparing metadata (setup.py) ... done
Using legacy 'setup.py install' for mysqlclient, since package 'wheel' is not installed.
Installing collected packages: mysqlclient
Running setup.py install for mysqlclient ... error
ERROR: Command errored out with exit status 1:
command: /Users/mac/Desktop/Autotestplat/venv/bin/python -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/private/var/folders/rv/405qccgj5f53d6kl66zhzxnw0000gn/T/pip-install-v2jjx4kq/mysqlclient_26381edf041844fe81f195c2171ff75f/setup.py'"'"'; file='"'"'/private/var/folders/rv/405qccgj5f53d6kl66zhzxnw0000gn/T/pip-install-v2jjx4kq/mysqlclient_26381edf041844fe81f195c2171ff75f/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(file) if os.path.exists(file) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, file, '"'"'exec'"'"'))' install --record /private/var/folders/rv/405qccgj5f53d6kl66zhzxnw0000gn/T/pip-record-q8mrkt6p/install-record.txt --single-version-externally-managed --compile --install-headers /Users/mac/Desktop/Autotestplat/venv/include/site/python3.6/mysqlclient
cwd: /private/var/folders/rv/405qccgj5f53d6kl66zhzxnw0000gn/T/pip-install-v2jjx4kq/mysqlclient_26381edf041844fe81f195c2171ff75f/
Complete output (35 lines):
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/distutils/dist.py:261: UserWarning: Unknown distribution option: 'long_description_content_type'
warnings.warn(msg)
running install
running build
running build_py
creating build
creating build/lib.macosx-10.6-intel-3.6
creating build/lib.macosx-10.6-intel-3.6/MySQLdb
copying MySQLdb/init.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb
copying MySQLdb/exceptions.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb
copying MySQLdb/compat.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb
copying MySQLdb/connections.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb
copying MySQLdb/converters.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb
copying MySQLdb/cursors.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb
copying MySQLdb/release.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb
copying MySQLdb/times.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb
creating build/lib.macosx-10.6-intel-3.6/MySQLdb/constants
copying MySQLdb/constants/init.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb/constants
copying MySQLdb/constants/CLIENT.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb/constants
copying MySQLdb/constants/CR.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb/constants
copying MySQLdb/constants/ER.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb/constants
copying MySQLdb/constants/FIELD_TYPE.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb/constants
copying MySQLdb/constants/FLAG.py -> build/lib.macosx-10.6-intel-3.6/MySQLdb/constants
running build_ext
building 'MySQLdb._mysql' extension
creating build/temp.macosx-10.6-intel-3.6
creating build/temp.macosx-10.6-intel-3.6/MySQLdb
gcc -fno-strict-aliasing -Wsign-compare -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g -Dversion_info=(1,4,2,'post',1) -Dversion=1.4.2.post1 -I/usr/local/mysql-5.7.31-macos10.14-x86_64/include -I/Users/mac/Desktop/Autotestplat/venv/include -I/Library/Frameworks/Python.framework/Versions/3.6/include/python3.6m -c MySQLdb/_mysql.c -o build/temp.macosx-10.6-intel-3.6/MySQLdb/_mysql.o
gcc -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g build/temp.macosx-10.6-intel-3.6/MySQLdb/_mysql.o -L/usr/local/mysql-5.7.31-macos10.14-x86_64/lib -lmysqlclient -o build/lib.macosx-10.6-intel-3.6/MySQLdb/_mysql.cpython-36m-darwin.so
ld: warning: The i386 architecture is deprecated for macOS (remove from the Xcode build setting: ARCHS)
ld: warning: ignoring file /usr/local/mysql-5.7.31-macos10.14-x86_64/lib/libmysqlclient.dylib, building for macOS-i386 but attempting to link with file built for macOS-x86_64
ld: warning: ignoring file /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/libSystem.tbd, missing required architecture i386 in file /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/libSystem.tbd (3 slices)
ld: dynamic main executables must link with libSystem.dylib for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: command 'gcc' failed with exit status 1
----------------------------------------
ERROR: Command errored out with exit status 1: /Users/mac/Desktop/Autotestplat/venv/bin/python -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/private/var/folders/rv/405qccgj5f53d6kl66zhzxnw0000gn/T/pip-install-v2jjx4kq/mysqlclient_26381edf041844fe81f195c2171ff75f/setup.py'"'"'; __file='"'"'/private/var/folders/rv/405qccgj5f53d6kl66zhzxnw0000gn/T/pip-install-v2jjx4kq/mysqlclient_26381edf041844fe81f195c2171ff75f/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(file) if os.path.exists(file) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file_, '"'"'exec'"'"'))' install --record /private/var/folders/rv/405qccgj5f53d6kl66zhzxnw0000gn/T/pip-record-q8mrkt6p/install-record.txt --single-version-externally-managed --compile --install-headers /Users/mac/Desktop/Autotestplat/venv/include/site/python3.6/mysqlclient Check the logs for full command output.
(venv) macdeMacBook-Pro-6:DiffTestPlatform mac$
这个 jenkins 是测试环境地址,不是本机的吧?
大佬,咨询个问题,公司没有使用 jenkins 部署,使用的是云效部署项目的,请问这个怎么搞?
楼主,感谢分享~
想问下通过 jacoco 可以知道变更代码的影响范围吗?
支持 python 下 针对 diff 文件进行覆盖统计吗
上面两个图的名称不一样,无法反向生成 url 吧。
部署了一波,使用了下:针对覆盖了报告,大神为啥没在 index 页面放个入口呢?
这个要支持一下,有支持 c# 代码的计划吗?
这个具体网上都有教程,jenkins 上下载 jacoco 插件,然后创建一个任务,在任务中实现拉取源代码、编译代码和覆盖率文件,通过 jacoco 插件分析这些数据来获取覆盖率详情。
可以作为检测开发自测的一种手段了
jenkins 上创建的覆盖率任务 -- 这个怎么弄的?
感谢分享,最近也想实践下~~
理论上这个 merge 结果不就是你想要的吗,100 行第一次覆盖了,第二次因为代码改动,所以统计为没覆盖。
嗯。有什么办法可以解决这种情况吗?想最后拿一个 merge 完了的报告去找开发一块讨论这块没有覆盖到的代码才有意义。不然一个一个看的话,开销太大了
我们全量覆盖率是 通过 jenkins 的 jacoco 插件来生成的,就是你需要在 jenkins 上面创建一个覆盖率任务用来生成全量覆盖率报告。而我们代码变更覆盖率报告就是基于这里生成的全量覆盖率报告而来的。
其中我们这里配置的覆盖率任务名称是为了得到对应覆盖率报告的路径。服务发布的任务名称也是为了在服务发布路径目录下获取 git 信息,从而得到当前 commit 的版本和比对版本的代码差异。
大神,想问下你们是怎么跟 jenkins、git 关联起来的,看你 github 上只是说 “任务名称为 jenkins 上创建的覆盖率任务的名称。发布名称为 jenkins 上服务发布的任务名称,比对版本为 git 上对应的版本的 commit 版本号 “ 这怎么关联
针对这个问题我之前也查过资料,我是这么理解的。生成 jacoco 报告的时候是会比对覆盖率文件和编译文件,假如两者代码不一致就不会显示覆盖。这就是为什么,明明你测试了,但是你覆盖率代码和编译代码不一致的情况下生成的覆盖率报告数据为空的情况了。所以即使你合并了覆盖率文件,当发现 100 行在覆盖率文件中虽然覆盖了,但是代码与源代码不一致,仍然不会显示为覆盖。
但是如果我在 v1 的时候生成的 exec,测试了 A 文件的 1-100 行,如果修改 bug 改了这 100 行,接着我测试了 101-200 行的 case,生成了 v2 的 exec。现在 v2 的 exec 和 v1 的 exec 对于 0-100 行的映射就不一样了吧。这种情况怎么用 merge 最后生成一个总的覆盖率报告呢?
首先 jacoco 的统计的覆盖率情况是根据拉取 exec 文件来的,如果你要 v2 的报告也要统计 v1 的覆盖率数据,你可以在 jacoco 的 build.xml 文件里对每次拉取的 exec 进行 merge。这样统计出来的是历史以来的全量的覆盖率情况。如下:
<target name="merge_exec">
<jacoco:merge destfile="/jenkins/workspace/xxxxxx/merged.exec">
<fileset dir="/jenkins/workspace/xxxxxx" includes="*.exec" />
</jacoco:merge>
</target>
当然在你在获取所有版本全量的覆盖率的情况下,再针对其中某个版本到最新版本的覆盖率,可以通过我的平台来生成。但是如果你直接要做整合多次覆盖率报告,只要修改 build.xml 就可以了。
想请问一下这个需求可以 cover 不,我在版本 v1 发布了一个应用,然后手工测试了这个版本的应用,jacoco 也有了报告 r1。然后开发修改了一些 bug,发布了 v2 的应用。经过测试后 jacoco 有了报告 r2.请问 r2 可以记录下我 r1 的报告做一个 merge 吗?
项目在 github 的 readme 上有说明:任务名称为 jenkins 上创建的覆盖率任务的名称。发布名称为 jenkins 上服务发布的任务名称,比对版本为 git 上对应的版本的 commit 版本号。你新增任务的时候按照这个规范输入就好
请问下,任务名称、发布名称,具体怎么配置啊 ?
这个是只针对 android 端的吗?
马克,借鉴下
非常好,点个赞,点亮小星星
目前我上传的平台代码是只针对在同一台机器上的。当然因为我们的测试环境也部署了多个 jenkins,所以也会存在你说的服务需要去调用其他机器上的 jenkins 的情况,不过这部分代码我没放到 github 上。这块你可以考虑用把平台中获取覆盖率的数据和报告的代码提取出来部署在 jenkins 的服务器上,然后在平台上通过 python 的 paramiko 库去实现远程执行 linux 命令去触发覆盖率数据的获取和报告生成,再通过这个库的文件下载功能把覆盖率文件拉下来。不过这样子执行效率会比正常的慢一点。
Jenkins 和服务的部署需要在同一个机器上吗?如果不是,配置能否详细介绍一下,谢谢
超级赞,给个好评再细看
有没有针对 PHP 代码实现的例子呢?全是 java 的,参考不到啊