Jmeter输出报告转allure2

最近发现公司很多是jmeter进行的自动化测试,存在的问题:原生报告巨丑无比,并且还没有那么友好的展示详情。

话不多说,开搞。

首先是jmeter配置的调整,需要拿到整个请求的详情

1、找到jmeter bin目录下的jmeter.properties修改配置如下


2、找到jmeter bin目录下的修改user.properties添加配置

jmeter.save.saveservice.output_format=xml

jmeter.save.saveservice.response_data=true

jmeter.save.saveservice.samplerData=true

jmeter.save.saveservice.requestHeaders=true

jmeter.save.saveservice.url=true

jmeter.save.saveservice.responseHeaders=true

3、通过/u01/jmeter/apache-jmeter-5.3/bin/jmeter.sh -n -t xxx.jmx -l result.xml -j test.log 输出报告


这个时候就会发现输出的xml报告包含了用例的层级和详情信息

<httpSample t="1" lt="1" ts="1450684950333" s="true" lb="app.testdelay" rc="200" rm="OK" tn="appdelay-3000g3m 1-1" dt="" by="2265"/>

t表示从请求开始到响应结束的时间

lt表示整个的空闲时间

ts表示访问的时刻

s表示返回的结果true表示成功,false表示失败

lb表示标题

rc表示返回的响应码

rm表示响应信息

tn表示线程的名字“1-138”表示第1个线程组的第138个线程。

dt表示响应的文件类型

by表示请求和响应的字节数

到这里差不多完成一半了,有这个xml,我把它解析成自己想要的pytest allure不就行了

4、把xml报告处理成pytest,python3代码如下

        #通过命令行来执行 ,例如python3 dealReportXml.py result.xml test_demo.py report/resource

# -*- coding: utf-8 -*-

# @Time    : 2020/11/30 4:29 下午

import xml.etree.cElementTreeas ET

import json,os,uuid

import sys

# xmlObject xml工程

# checkString 这个其实是固定值 httpSample,只不过后面想要获取其他结构参数化

# num 当时想嵌套多层,后来发现allure只三层结构,其实这个也没啥用

# result 传递解析的xml

# demoFile 生成的pytes文件

# featureIndex 用来排序参数用例,按照jmeter中的树结果

# storyIndex 同上,是第二层级的排序

def checkChildren(xmlObject, checkString, num, result, demoFile, featureIndex, storyIndex):

    for childrenin xmlObject:

        try:

            if num == 1 and children.attrib['sby'] != "0":

                featureIndexStr='#'+str(featureIndex)+" " if featureIndex >= 10 else '#0'+str(featureIndex)+" "

                result["feature"] = featureIndexStr+children.attrib['lb']

                featureIndex += 1

            if num >= 2 and children.tag== "sample":

                storyIndexStr= '#' + str(storyIndex) + " " if storyIndex >= 10 else '#0' + str(

                    storyIndex) + " "

                result["story"] = storyIndexStr+children.attrib['lb']

                storyIndex += 1

        except:

            pass

        if children.tag== checkString:

            result['case_name'] = children.attrib['lb']

            for httpSampleChildrenin children:

                result[httpSampleChildren.tag] = httpSampleChildren.text

if httpSampleChildren.tag== 'assertionResult':

                    for assertionResultChildrenin httpSampleChildren:

                        result[assertionResultChildren.tag] = assertionResultChildren.text

feature= result['feature'] if "feature" in result else None

            story= result['story'] if 'story' in result else None

            case_name= result['case_name'] if 'case_name' in result else None

            URL= result['java.net.URL'] if 'java.net.URL' in result else None

            method= result['method'] if 'method' in result else None

            requestHeader= result['requestHeader'] if 'requestHeader' in result else None

            queryString= result['queryString'] if 'queryString' in result else None

            responseData= result['responseData'] if 'responseData' in result else None

            failureMessage= result['failureMessage'] if 'failureMessage' in result else None

            failure= result['failure'] if 'failure' in result else "false"

            print(feature,story,case_name,failureMessage,URL)

            storyString= "@allure.story('"+result['story']+"')  # 二级目录" if 'story' in result else ''

            pyString= '''

@allure.feature('{feature}')  # 一级目录{story}

@allure.title("{case_name}")

def test_allure_report_{num}():

    with allure.step('请求url:{URL}'):

        print('请求url:{URL}')

    with allure.step('请求方法:{method}' ):

        print('请求方法:{method}' )

    with allure.step('请求头:{requestHeader}' ):

        print('请求头:{requestHeader}' )

    with allure.step(\'''请求数据:{queryString}\'''):

        print(\'''请求数据:{queryString}\''')

    with allure.step('接口返回:{responseData}'):

        print('接口返回:{responseData}')

    with allure.step('断言结果:{failureMessage}'):

        print('断言结果:{failureMessage}')

    assert "{failure}" == 'false' '''.format(feature=feature,story=storyString,case_name=case_name,

                                            num=str(uuid.uuid1()).replace('-',''),

                                          URL=URL,method=method, requestHeader=str(requestHeader).replace('\n', '').replace('\r', ''),

                                            queryString=str(json.dumps(queryString)).replace("'","\""),

                                            responseData=str(json.dumps(responseData)).replace("'","\""),

                                          failureMessage=str(failureMessage).replace("'","\""),failure=failure)

            print(pyString)

            with open(demoFile,'a') as c:

                c.write(pyString)

        else:

            checkChildren(children,checkString,num+1,result,demoFile,featureIndex,storyIndex)

if __name__== '__main__':

    #通过命令行来执行 ,例如python3 dealReportXml.py result.xml test_demo.py report/resource

    commonndLines= sys.argv

print(commonndLines)

    with open(commonndLines[2], "w") as demo:

        demo.write('''

# -*- coding: utf-8 -*-

import allure

            ''')

    tree= ET.parse(commonndLines[1])

    root= tree.getroot()

    checkChildren(root,"httpSample",1,{},commonndLines[2],1,1)

    pyString= 'pytest --capture=no '+commonndLines[2]+' --alluredir '+commonndLines[3]

    os.system(pyString)

    alSring= "allure generate "+commonndLines[3]+" -o report/html --clean"

    os.system(alSring)

5、打开生成的报告


细心的你会发现,这里面的时间是错误的,别急,我们来处理一下。

回头看看jmeter报告中的协议

t表示从请求开始到响应结束的时间

lt表示整个的空闲时间

ts表示访问的时刻

s表示返回的结果true表示成功,false表示失败

lb表示标题

rc表示返回的响应码

rm表示响应信息

tn表示线程的名字“1-138”表示第1个线程组的第138个线程。

dt表示响应的文件类型

by表示请求和响应的字节数

只要我们把对应的时间替换报告中对应的时间即可

6、优化 把jmeter的实际执行时间匹配进去,最终版本,如下

直接执行命令

 python3 dealReportXml.py result.xml test_demo.py report/resource

allure generate report/resource -o report/html --clean

完整代码如下

# -*- coding: utf-8 -*-

# @Time    : 2020/11/30 4:29 下午

import xml.etree.cElementTreeas ET

import json,os,uuid

import sys

from osimport listdir

from os.pathimport isfile, join

# xmlObject xml工程

# checkString 这个其实是固定值 httpSample,只不过后面想要获取其他结构参数化

# num 当时想嵌套多层,后来发现allure只三层结构,其实这个也没啥用

# result 传递解析的xml

# demoFile 生成的pytes文件

# featureIndex 用来排序参数用例,按照jmeter中的树结果

# storyIndex 同上,是第二层级的排序

# timeChekout 用来替换allure报告中的时间为jmeter报告时间

def checkChildren(xmlObject, checkString, num, result, demoFile, featureIndex, storyIndex,timeChekout):

    for childrenin xmlObject:

        try:

            if num == 1 and children.attrib['sby'] != "0":

                featureIndexStr='#'+str(featureIndex)+" " if featureIndex >= 10 else '#0'+str(featureIndex)+" "

                result["feature"] = featureIndexStr+children.attrib['lb']

                featureIndex += 1

            if num >= 2 and children.tag== "sample":

                storyIndexStr= '#' + str(storyIndex) + " " if storyIndex >= 10 else '#0' + str(

                    storyIndex) + " "

                result["story"] = storyIndexStr+children.attrib['lb']

                storyIndex += 1

        except:

            pass

        if children.tag== checkString:

            result['caseName'] = children.attrib['lb']

            for httpSampleChildrenin children:

                if httpSampleChildren.tag== 'assertionResult':

                    assertionResultChildrenTag=[]

                    for assertionResultChildrenin httpSampleChildren:

                        assertionResultChildrenTag.append(assertionResultChildren.tag)

                        result[assertionResultChildren.tag] = assertionResultChildren.text

if "failureMessage" not in assertionResultChildrenTag:

                        result['failureMessage'] = None

                else:

                    result[httpSampleChildren.tag] = httpSampleChildren.text

feature= result['feature'] if "feature" in result else None

            caseName= result['caseName'] if 'caseName' in result else None

            URL= result['java.net.URL'] if 'java.net.URL' in result else None

            method= result['method'] if 'method' in result else None

            requestHeader= result['requestHeader'] if 'requestHeader' in result else None

            queryString= result['queryString'] if 'queryString' in result else None

            responseData= result['responseData'] if 'responseData' in result else None

            failureMessage= result['failureMessage'] if 'failureMessage' in result else None

            failure= result['failure'] if 'failure' in result else "false"

            menthodUuid= str(uuid.uuid1()).replace('-','')

            start= int(children.attrib['ts'])

            stop= int(children.attrib['ts'])+int(children.attrib['t'])

            timeChekout["test_demo#test_allure_report_"+menthodUuid]={"start":start,"stop":stop}

            storyString= "@allure.story('"+result['story']+"')  # 二级目录" if 'story' in result else ''

            pyString= '''

@allure.feature('{feature}')  # 一级目录{story}

@allure.title("{caseName}")

def test_allure_report_{num}():

    with allure.step('请求url:{URL}'):

        print('请求url:{URL}')

    with allure.step('请求方法:{method}' ):

        print('请求方法:{method}' )

    with allure.step('请求头:{requestHeader}' ):

        print('请求头:{requestHeader}' )

    with allure.step(\'''请求数据:{queryString}\'''):

        print(\'''请求数据:{queryString}\''')

    with allure.step('接口返回:{responseData}'):

        print('接口返回:{responseData}')

    with allure.step('断言结果:{failureMessage}'):

        print('断言结果:{failureMessage}')

    assert "{failure}" == 'false' '''.format(feature=feature,story=storyString,caseName=caseName,

                                            num=menthodUuid,URL=URL,method=method, requestHeader=str(requestHeader).replace('\n', '').replace('\r', ''),

                                            queryString=str(json.dumps(queryString)).replace("'","\""),

                                            responseData=str(json.dumps(responseData)).replace("'","\""),

                                            failureMessage=str(failureMessage).replace("'","\""),failure=failure)

            with open(demoFile,'a') as c:

                c.write(pyString)

        else:

            checkChildren(children,checkString,num+1,result,demoFile,featureIndex,storyIndex,timeChekout)

    return timeChekout

if __name__== '__main__':

    #通过命令行来执行 ,例如python3 dealReportXml.py result.xml test_demo.py report/resource

    commonndLines= sys.argv

with open(commonndLines[2], "w") as demo:

        demo.write('''

# -*- coding: utf-8 -*-

import allure

            ''')

    tree= ET.parse(commonndLines[1])

    root= tree.getroot()

    realQueryTime= checkChildren(root,"httpSample",1,{},commonndLines[2],1,1,{})

    pyString= 'pytest --capture=no '+commonndLines[2]+' --alluredir '+commonndLines[3]

    os.system(pyString)

    files= [mfor min listdir(commonndLines[3]+"/") if isfile(join(commonndLines[3]+"/", m))]

    for filein files:

        if ".json" in file:

            try:

                with open(commonndLines[3]+"/" + file, 'r') as f:

                    jsonStr= json.loads(f.read())

                    start= realQueryTime[jsonStr['fullName']]["start"]

                    stop= realQueryTime[jsonStr['fullName']]["stop"]

                    jsonStr['start'] = start

jsonStr['stop'] = stop

with open(commonndLines[3]+"/" + file, "w") as m:

                        json.dump(jsonStr, m, indent=2, sort_keys=True, ensure_ascii=False)  # 写为多行

            except:

                pass



打开报告看时间和jmeter报告中一样,有没有很爽



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容