Jmeter——結合Allure展示測試報告

在平時用jmeter做測試時,生成報告的模板,不是特別好。大家應該也知道allure報告,頁面美觀。

先來看效果圖,報告首頁,如下所示:

報告詳情信息,如下所示:

運行run.py文件,運行成功,如下所示:

接下來來看下實現過程。

安裝allure

allure是開源的,直接到github上下載即可。就不細說了。需要注意的是,環境變量的配置,allure的bin路徑,需要配置到環境變量path中。

jmeter配置

找到bin目錄下的 jmeter.properties 配置文件,將如下所示對應配置取消注釋,jmeter.save.saveservice.output_format 修改為xml。

# This section helps determine how result data will be saved.
# The commented out values are the defaults.

# legitimate values: xml, csv, db.  Only xml and csv are currently supported.
jmeter.save.saveservice.output_format=xml

# The below properties are true when field should be saved; false otherwise
#
# assertion_results_failure_message only affects CSV output
#jmeter.save.saveservice.assertion_results_failure_message=true
#
# legitimate values: none, first, all
#jmeter.save.saveservice.assertion_results=none
#
jmeter.save.saveservice.data_type=true
jmeter.save.saveservice.label=true
jmeter.save.saveservice.response_code=true
# response_data is not currently supported for CSV output
jmeter.save.saveservice.response_data=true
# Save ResponseData for failed samples
jmeter.save.saveservice.response_data.on_error=true
jmeter.save.saveservice.response_message=true
jmeter.save.saveservice.successful=true
jmeter.save.saveservice.thread_name=true
jmeter.save.saveservice.time=true
jmeter.save.saveservice.subresults=true
jmeter.save.saveservice.assertions=true
jmeter.save.saveservice.latency=true
# Only available with HttpClient4
jmeter.save.saveservice.connect_time=true
jmeter.save.saveservice.samplerData=true
jmeter.save.saveservice.responseHeaders=true
jmeter.save.saveservice.requestHeaders=true
jmeter.save.saveservice.encoding=true
jmeter.save.saveservice.bytes=true
# Only available with HttpClient4
jmeter.save.saveservice.sent_bytes=true
jmeter.save.saveservice.url=true
jmeter.save.saveservice.filename=true
jmeter.save.saveservice.hostname=true
jmeter.save.saveservice.thread_counts=true
jmeter.save.saveservice.sample_count=true
jmeter.save.saveservice.idle_time=true

# Timestamp format - this only affects CSV output files
# legitimate values: none, ms, or a format suitable for SimpleDateFormat
jmeter.save.saveservice.timestamp_format=ms
jmeter.save.saveservice.timestamp_format=yyyy/MM/dd HH:mm:ss.SSS

生成jmeter結果文件

使用命令 jmeter -n -t C:\Users\Desktop\auth.jmx -l C:\Users\Desktop\result.xml 即可生成xml文件

安裝依賴包

按項目中的 requirements.txt 文件,安裝對應的依賴包即可。

文件解析生成allure報告

文件解析

xml文件內容如下:

從上述的內容,我們可以分析得出如下內容:

t 從請求開始到響應結束的時間
ts 表示訪問的時刻: date
s 運行的結果
lb 表示標題
rc 返回的響應碼
rm 響應信息
tn 線程的名字
assertionResult: 斷言信息
responseData/samplerData: 返回數據
queryString: 請求信息

代碼實現

利用生成的結果文件生成pytest的參數化數據

    try:
        converte_data = xmltodict.parse(result_file, encoding='utf-8')
        sample_keys = list(converte_data['testResults'].keys())
        result = []
        ws_result = []
        sample_result = converte_data['testResults']['httpSample'] if isinstance(
            converte_data['testResults']['httpSample'],
            list) else [converte_data['testResults']['httpSample']]
        if 'sample' in sample_keys:
            ws_result = converte_data['testResults']['sample'] if isinstance(converte_data['testResults']['sample'],
                                                                             list) else [
                converte_data['testResults']['sample']]
        result_data = sample_result + ws_result
        for data in result_data:
            time = data['@t'] if '@t' in data else ''
            date = data['@ts'] if '@ts' in data else ''
            status = data['@s'] if '@s' in data else ''
            title = data['@lb'] if '@lb' in data else ''
            status_code = data['@rc'] if '@rc' in data else ''
            status_message = data['@rm'] if '@rm' in data else ''
            thread = data['@tn'] if '@tn' in data else ''
            assertion = data['assertionResult'] if 'assertionResult' in data else ''
            response_data = data['responseData']['#text'] if 'responseData' in data and '#text' in data['responseData'] \
                else ''
            sampler_data = data['samplerData']['#text'] if 'samplerData' in data and '#text' in data['samplerData'] \
                else ''
            request_data = data['queryString']['#text'] if 'queryString' in data and '#text' in data[
                'queryString'] else ''
            request_header = data['requestHeader']['#text'] if 'requestHeader' in data and '#text' in data[
                'requestHeader'] else ''
            request_url = data['java.net.URL'] if 'java.net.URL' in data else ''
            story = '未標記'
            assertion_name, assertion_result = None, None
            if status == 'false':
                assertion_name, assertion_result = get_assertion(assertion)

            meta_data = (
                time, date, status, story, title, status_code, status_message, thread, assertion_name, assertion_result,
                response_data
                , sampler_data, request_data, request_header, request_url)
            result.append(meta_data)
        return result
    except Exception as e:
        print(e)
        return [
            ('time', 'date', 'true', 'story', 'title', 'status_code', 'status_message', 'thread', 'assertion_name',
             'assertion_result',
             'response_data', 'sampler_data', 'request_data', 'request_header', 'request_url')]

用例詳情字段

@allure.step('用例名稱:{title}')
    def title_step(self, title):
        pass

    @allure.step('請求信息')
    def request_step(self, request_url, request_header, request_data, sampler_data):
        pass

    @allure.step('斷言信息')
    def assert_step(self, assertion_name, assertion_result):
        assert False

    @allure.step('文件信息:{thread}')
    def file_step(self, thread):
        pass

    @allure.step('返回信息')
    def response_step(self, status_code, status_message, response_data):
        pass

    @allure.step('附件(全部信息)')
    def attach_all(self, data):
        allure.attach(str(data), name='attach_all_data',
                      attachment_type=allure.attachment_type.JSON)

    def base_step(self, time, date, status, title, status_code, status_message, thread, assertion_name,
                  assertion_result, response_data,
                  sampler_data, request_data, request_header,
                  request_url):
        data = {'title': title, 'thread': thread, 'request_url': request_url, 'request_header': request_header,
                'request_data': request_data, 'sampler_data': sampler_data, 'status_code': status_code,
                'response_data': response_data, 'assertion_name': assertion_name, 'assertion_resul': assertion_result}
        self.file_step(thread)
        self.title_step(title)
        self.request_step(request_url, request_header, request_data, sampler_data)
        self.response_step(status_code, status_message, response_data)
        self.attach_all(data)
        if status == 'false':
            self.assert_step(assertion_name, assertion_result)
            assert False
        else:
            assert True

    @allure.title("{title}")
    @allure.feature("失敗信息")
    @pytest.mark.parametrize(
        "time,date,status,story,title,status_code,status_message,thread,assertion_name,assertion_result,response_data,sampler_data,request_data,request_header,"
        "request_url",
        xml_2_data(type=1))
    def test_gjw(self, time, date, status, story, title, status_code, status_message, thread, assertion_name,
                    assertion_result,
                    response_data, sampler_data, request_data, request_header,
                    request_url):
        # allure.dynamic.story(story)
        self.base_step(time, date, status, title, status_code, status_message, thread, assertion_name, assertion_result,
                       response_data,
                       sampler_data, request_data, request_header,
                       request_url)

處理報告轉化時間一致

def report_edit(env):
    path = os.path.join(Path().get_report_path(), env, 'data')
    # 批量更新聚合文件
    for file in os.listdir(path):
        if '.json' in file and 'categories' not in file:
            try:
                with open(os.path.join(path, file), 'r') as f:
                    json_str = json.loads(f.read())
                    for data in json_str['children'][0]['children']:
                        name = data['name']
                        for meta in result:
                            if name == meta[3]:
                                data['time']['start'] = int(meta[1])
                                data['time']['stop'] = int(meta[1]) + int(meta[0])
                                data['time']['duration'] = int(meta[0])
                    with open(os.path.join(path, file), 'w') as w:
                        json.dump(json_str, w, indent=2, sort_keys=True, ensure_ascii=False)
            except Exception as e:
                print(e)
    # 批量更新case文件
    cases_path = os.path.join(path, 'test-cases')
    for file in os.listdir(cases_path):
        if '.json' in file and 'categories' not in file:
            try:
                with open(os.path.join(cases_path, file), 'r') as f:
                    json_str = json.loads(f.read())
                    name = json_str['name']
                    for meta in result:
                        if name == meta[3]:
                            json_str['time']['start'] = int(meta[1])
                            json_str['time']['stop'] = int(meta[1]) + int(meta[0])
                            json_str['time']['duration'] = int(meta[0])
                    with open(os.path.join(cases_path, file), 'w') as w:
                        json.dump(json_str, w, indent=2, sort_keys=True, ensure_ascii=False)
            except Exception as e:
                print(e)

上述只是部分代碼,完整代碼已上傳,JmeterAllureReport ,有興趣的可以再完善。