基于jacoco插件,使用python脚本分析java项目测试覆盖率。

1. 需求分析:

1.1 功能描述

  1. 在java项目中要得出测试的覆盖率,使用jacoco插件,利用其生成的html文件进行解析。在脚本中生成一个以字符串形式构成的html格式的文件。
  2. 覆盖率以模块为单位进行表示,并且每一个模块中需要分析到包的覆盖率。
  3. 覆盖率的内容可以有全部,也可以用一部分,我这里选用了行,类,方法,并且对表示形式做了一些优化。
  4. 需要有邮件发送的功能,以及对邮箱格式检查的功能。
  5. 需要以参数的形式传入阈值,并且在脚本中分析每一模块是否达到标准。

2. 项目说明

2.1 前期准备

2.1.1 java依赖

因为是SpringBoot项目,并且使用了jacoco的插件,代码仓库顶层pom.xml里面加入如下maven依赖,子模块只需继承(parent)父pom即可。(1. 又学了maven一遍,层次结构,groupid,等)

<dependency>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.7</version>
</dependency>

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.6</version>
    <executions>
        <execution>
            <id>prepare-agent</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.16</version>
    <configuration>
        <skip>false</skip>
        <testFailureIgnore>true</testFailureIgnore>
    </configuration>
</plugin>

这里稍作解释:

  1. 加入了jacoco的依赖。
  2. 加了jacoco的运行插件。
  3. 加了org.apache.maven.plugins插件(我也不确定加不加)
    org.apache.maven.plugins的功能解释

顶层pom中需要使用标签管理同个项目中的各个模块。因为是根据项目中的来获取模块名称的。所以必须添加。
示例:

    <modules>
        <module>modulename1</module>
        <module>modulename2</module>
    </modules>

2.2 python执行准备

因为我们需要给脚本传入三个参数,所以需要运行脚本时显式的传入参数:

python Soup.py https://code.hwwt2.com/enterprise__helloworld/Test.git 1253324157@qq.com 65%

3.代码说明

import json
import sys
import random
import requests
import shutil
import re
import os
from bs4 import BeautifulSoup
#python接收命令行参数

#接收GITURL和邮箱地址
gitUrl = sys.argv[1]
email = sys.argv[2]
flag = sys.argv[3]
flag = float(flag.strip("%"))
codeDir = ""
ti = True
#生成随机的15字符目录
for i in range(15):
    num = random.randint(0,9)
    letter = chr(random.randint(97, 122))  # 取小写字母
    s = str(random.choice([num,letter]))
    codeDir += s

if('/' in codeDir):
    print("目录中有非法字符")
    sys.exit()

#验证邮箱格式
emails = email.split(";")
print(emails)
def check_email_url(email_address):
    # check '@'
    at_count = 0
    for element in email_address:
        if element == '@':
            at_count = at_count + 1

    if at_count != 1:
        return 0

    # check ' '
    for element in email_address:
        if element == ' ':
            return 0

    # check '.com'
    postfix = email_address[-4:]
    if postfix != '.com':
        return 0

    # check char
    for element in email_address:
        if element.isalpha() == False and element.isdigit() == False:
            if element != '.' and element != '@' and element != '_':
                return 0
    return 1

for e in emails:
    print(e)
    if(check_email_url(e) == 0):
        print("邮箱格式出错")
        sys.exit()

#拉取代码
os.system("git clone " + gitUrl + " " + codeDir)
os.system("cd " + codeDir + " && mvn test -Dmaven.test.failure.ignore=true && cd ../")
try:

    #先解析pom,拿到moduleList
    pom = open(codeDir + '/pom.xml').read()
    pomsoup = BeautifulSoup(pom,"xml")
    ps = pomsoup.find_all('module')
    moduleList = []
    for i in ps:
        moduleList.append(i.string)
    print(len(moduleList))
    number = len(moduleList)

    result = "<p>本次单元测试报告如下所示,请查阅。本报告以模块为单位展示单元测试覆盖率及报告,覆盖率阈值为 : "+ str(flag) + "% </p>"

    #对每一个模块的文件进行导入解析
    for r in range(number):
        add = codeDir + '/' +moduleList[r] + "/target/site/jacoco/index.html"
        #add =  i + "\\target\\site\\jacoco\\index.html"
        f = open(add).read()
        soup = BeautifulSoup(f, "html.parser")


        #解析模块名
        moduleName = soup.find('h1').string

        result += '<h3>' +"模块名称:" + moduleName + '</h3>'
        #result += '</br>'
        result += '<table border="1px" style="border-collapse:collapse;">'
        #解析thead
        result += '<thead style="font-weight: bold;">'
        result += '<tr>'
        heads = soup.find('thead')

        #因为不需要前面几个元素,所以从第一个后进行一个截断
        result += str(heads.find('td'))
        headTd = heads.find_all('td')[7:14]
        for td in range(3):
            result += '<td>' + headTd[td*2+1].string + ", %"+ '</td>'
        result += '</tr>'
        result += '</thead>'

        #解析tbody
        result += '<tbody>'
        body = soup.find('tbody')
        bodyTr = body.find_all('tr')
        for tds in bodyTr:
            result += '<tr>'
            result += str(tds.find('td'))
            tds = tds.find_all('td')[7:14]
            # for td1 in tds:
            #     result += str(td1)
            for i in range(3):
                bodyNumber = tds[i*2].string
                bodyNumber1 = tds[i*2+1].string
                bodyResult = "{:.2%}".format((int(bodyNumber1) - int(bodyNumber)) / int(bodyNumber1))
                result += '<td>' + str(bodyResult) + ' (' + str(int(bodyNumber1) - int(bodyNumber)) +'/' + bodyNumber1 + ")"+  "</td>"
            result += '</tr>'
        result += '</tbody>'

        #解析tfoot
        result += '<tfoot>'
        result += '<tr>'
        foot = soup.find('tfoot')
        result += str(foot.find('td'))
        footTd = foot.find_all('td')[7:14]

        #计算行覆盖率,并且判断是否达到标准
        line = footTd[1].string
        missed = footTd[0].string
        fin = "{:.2%}".format((int(line) - int(missed)) / int(line))
        res = float(fin.strip("%"))
        for td in range(3):
            tfootNumber = footTd[td*2].string
            tfootNumber1 = footTd[td*2+1].string
            tfootResult = "{:.2%}".format((int(tfootNumber1) - int(tfootNumber)) / int(tfootNumber1))
            result += '<td>' + str(tfootResult) + ' (' + str(int(tfootNumber1) - int(tfootNumber)) + '/' + tfootNumber1 + ")" + "</td>"
        result += '</tr>'
        result += '</tfoot>'
        if(res < flag):
            result += '<h4  style="color: red;">' + "模块的行覆盖率:" + fin + "   未通过" +'</h4>'
            ti = False
        else:
            result += '<h4>' + "模块的行覆盖率:" + fin +  "   通过" +'</h4>'
        result += '</table>'
        result += '<table>'
        #加入txt文件的内容

        for root, dirs, files in os.walk(codeDir + '/' +moduleList[r] + "/target/surefire-reports/"):
            for f in files:
                if (os.path.join(root, f).__contains__(".txt") == True):
                    file = os.path.join(root, f)
                    for res in open(file,encoding='utf-8').readlines():
                        if (res.__contains__("Test set") or res.__contains__("<<< ERROR!") or res.__contains__("Tests run")):
                            result += '<tr><td>' + res + '</td></tr>'
        result += '</table>'
        result += '</br>'
    result += "</table>"
except Exception:
    shutil.rmtree(codeDir)
    print("已经删除了测试覆盖率报告相应的内容")

#使用qq发送邮件
sent=smtplib.SMTP()
sent.connect('smtp.qq.com',25)
mail_name="" # 发送人邮箱地址
mail_password = "cqydsoobovdahghj" # 注意:这里不是密码,而应该填写授权码
sent.login(mail_name, mail_password) # 登陆

# 编辑邮件内容
#不知道为什么只有当MIMEText的第二个参数是plain的时候才有正文
to = [''] # 收件人邮箱地址
content = MIMEText(result, 'html', 'utf-8') # 正文内容
content['Subject'] = '代码测试覆盖率报告' # 邮件标题
content['From'] = mail_name # 发件人
content['To'] =','.join(to) #收件人,用逗号连接多个邮件,实现群发

try:
    sent.sendmail(mail_name, to, content.as_string())  #3个参数 发送人,收件人,邮件内容
    print('Success')
    sent.close()
except smtplib.SMTPException:
    print("Error:Fail")
#邮件发送
url = "http://172.20.233.14:32433/mmpinterface/message/send_timely"
print(len(result.encode()))
result = json.dumps(result)
title = ""
if(ti == True):
    title = "【通过】 "+gitUrl+" 单元测试报告"
else:
    title =  "【未通过】 "+gitUrl+" 单元测试报告"
ebody = {
    "appId":87,
    "channelCode":"callback",
    "msgContent":"{\"target\":"+json.dumps(email)+",\"title\":"+json.dumps(title)+",\"lang\":\"cn\",\"templateId\":\"\",\"cc\":\"\",\"content\":"+result+"}",
    "msgType":1,
    "target": email,
    "templateCode":""
}
r = requests.post(url=url, json=ebody, headers={'Content-Type':'application/json','Host':'msgservice.vip.tp.local'})
print(r.text)
#删除目录
shutil.rmtree(codeDir)
print("已经删除了测试覆盖率报告相应的内容")
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值