前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >20.UI自动化测试框架搭建-标记性能较差用例

20.UI自动化测试框架搭建-标记性能较差用例

作者头像
zx钟
发布2022-12-02 15:36:31
3400
发布2022-12-02 15:36:31
举报
文章被收录于专栏:测试游记测试游记

在拿到性能数据后需要将数据利用起来,下面对性能数据进行分析

实现:如果性能达到设定的阈值,那么这段时间执行的用例就是性能较差的用例

确定阈值

首先确定一个阈值来当做性能的告警值,暂定为以下算法

# threshold 阈值
# average 平均值
# max 最大值 
threshold = average + (max - average) * 0.8

计算各项值

在上一章已经拿到了性能数据,然后把他们插入到数据库中

由于平台是Java的所以用Java编写

构造一个HashMap来存放全部的数据

Map<String, Map<String, List<Double>>> perfSummaryData = new HashMap<>();

最终返回的数据类型为:

{
"cpu":{"system":[,,121.43243243243244,314.6864864864865],"idle":[,,537.3716216216217,716.2743243243243],"pic_cpu":[,3.2,66.42430555555556,311.6848611111111],"device_cpu_rate":[,,245.6418918918919,772.3283783783784],"user":[,,124.20945945945945,546.4418918918919]},
"mem":{"free_ram":[2707.73,1720.11,2044.2376923076922,2575.0315384615387],"pid_pss":[452.18,231.72,313.105,424.365],"total_ram":[5478.93,5478.93,5478.929999999999,5478.93]},

"other":null,

"power":{"current":[,,,],"tempreture":[36.8,29.9,33.69638554216868,36.17927710843373],"voltage":[4.34,4.13,4.284096385542169,4.328819277108433]},

"pss":{"pssList":[],"java_heap":[],"pss_system":[],"native_heap":[]},

"thread":{"threadList":[,,191.46308724832215,255.89261744966444]},

"traffic":{"device_transport":[6283.15,433.69,3777.436774193548,5782.00735483871],"device_receive":[167860.62,11597.64,106851.58806451612,155658.81361290324],"device_total":[174143.77,12031.33,110629.02451612902,161440.8209032258],"pid_tx":[6283.15,474.8,3779.061612903226,5782.332322580645],"pid_total":[174143.71,13349.69,110675.72096774195,161450.11219354838],"pid_rx":[167860.55,12874.89,106896.65838709677,155667.77167741934]},

"version":"微医用户版4.6.5-内部版本号:268-灰度-待发布-创建时间:2022-06-23 21:18:10-最近修改时间:2022-06-23 21:18:10"
}

依次解析每个版本每次执行的性能数据然后根据性能类型放到对应的List中

然后计算一下最大、最小、平均、阈值

floats.add(floats1.stream().mapToDouble(e -> e).filter(e -> e > 0.0).max().orElse(0.0));
floats.add(floats1.stream().mapToDouble(e -> e).filter(e -> e > 0.0).min().orElse(0.0));
floats.add(floats1.stream().mapToDouble(e -> e).filter(e -> e > 0.0).average().orElse(0.0));
floats.add(floats.get() + 0.8 * (floats.get() - floats.get())); // 阈值

完整代码

@Override
public List<UiPerfSummaryDo> getPerfDataSummary(UiPerfPageQuery pageQuery) {
    List<UiPerfDo> perfData = uiReportMapper.getPerfData(pageQuery);
    Map<String, Map<String, List<Double>>> perfSummaryData = new HashMap<>();
    String[] cpuKeys = { "device_cpu_rate", "user", "system", "idle", "pic_cpu" };
    String[] memKeys = { "total_ram", "free_ram", "pid_pss" };
    String[] powerKeys = { "voltage", "tempreture", "current" };
    String[] threadKeys = { "threadList" };
    String[] pssKeys = { "pssList", "java_heap", "native_heap", "pss_system" };
    String[] trafficKeys = { "device_total", "device_receive", "device_transport", "pid_rx", "pid_tx",
        "pid_total" };
    String[][] allKeys = { cpuKeys, memKeys, powerKeys, threadKeys, pssKeys, trafficKeys };

    perfData.forEach(item -> {
        String version = item.getVersion();
        if (!perfSummaryData.containsKey(version)) {
            HashMap<String, List<Double>> perfVersion = new HashMap<>();
            for (String[] keys : allKeys) {
                for (String key : keys) {
                    perfVersion.put(key, new ArrayList<>());
                }
            }
            perfSummaryData.put(version, perfVersion);
        }
        Map<String, List<Double>> stringListMap = perfSummaryData.get(version);
        List<String> cpus = JSONArray.parseArray(item.getCpu(), String.class);
        List<String> mems = JSONArray.parseArray(item.getMem(), String.class);
        List<String> powers = JSONArray.parseArray(item.getPower(), String.class);
        List<String> threads = JSONArray.parseArray(item.getThread(), String.class);
        List<String> psss = JSONArray.parseArray(item.getPss(), String.class);
        List<String> traffics = JSONArray.parseArray(item.getTraffic(), String.class);
        cpus.forEach(cpu -> {
            List<String> oneCpuInfo = JSONArray.parseArray(cpu, String.class);
            if (!oneCpuInfo.isEmpty()) {
                for (int i = ; i < ; i++) {
                    List<Double> datalist = stringListMap.get(cpuKeys[i - ]);
                    datalist.add(Double.parseDouble(oneCpuInfo.get(i)));
                }
            }
        });
        mems.forEach(mem -> {
            List<String> oneMemInfo = JSONArray.parseArray(mem, String.class);
            if (!oneMemInfo.isEmpty()) {
                for (int i = ; i < ; i++) {
                    List<Double> datalist = stringListMap.get(memKeys[i - ]);
                    datalist.add(Double.parseDouble(oneMemInfo.get(i)));
                }
            }
        });
        powers.forEach(power -> {
            List<String> onePowerInfo = JSONArray.parseArray(power, String.class);
            if (!onePowerInfo.isEmpty()) {
                for (int i = ; i < ; i++) {
                    List<Double> datalist = stringListMap.get(powerKeys[i - ]);
                    datalist.add(Double.parseDouble(onePowerInfo.get(i)));
                }
            }
        });
        threads.forEach(thread -> {
            List<String> oneThreadInfo = JSONArray.parseArray(thread, String.class);
            if (!oneThreadInfo.isEmpty()) {
                for (int i = ; i < ; i++) {
                    List<Double> datalist = stringListMap.get(threadKeys[i - ]);
                    datalist.add(Double.parseDouble(oneThreadInfo.get(i)));
                }
            }
        });
        psss.forEach(pss -> {
            List<String> onePssInfo = JSONArray.parseArray(pss, String.class);
            if (!onePssInfo.isEmpty()) {
                for (int i = ; i < ; i++) {
                    List<Double> datalist = stringListMap.get(pssKeys[i - ]);
                    datalist.add(Double.parseDouble(onePssInfo.get(i)));
                }
            }
        });
        traffics.forEach(traffic -> {
            List<String> oneTrafficInfo = JSONArray.parseArray(traffic, String.class);
            if (!oneTrafficInfo.isEmpty()) {
                for (int i = ; i < ; i++) {
                    List<Double> datalist = stringListMap.get(trafficKeys[i - ]);
                    datalist.add(Double.parseDouble(oneTrafficInfo.get(i)));
                }
            }
        });
    });
    List<UiPerfSummaryDo> uiPerfSummaryDos = new ArrayList<>();
    List<Double> floats;
    for (String key : perfSummaryData.keySet()) {
        UiPerfSummaryDo uiPerfSummaryDo = new UiPerfSummaryDo();
        Map<String, List<Double>> stringListMap = perfSummaryData.get(key);
        uiPerfSummaryDo.setVersion(key);
        for (String[] all_key : allKeys) {
            HashMap<String, List<Double>> dataDict = new HashMap<>();
            for (String key1 : all_key) {
                floats = new ArrayList<>();
                List<Double> floats1 = stringListMap.get(key1);
                if (!floats1.isEmpty()) {
                    floats.add(floats1.stream().mapToDouble(e -> e).filter(e -> e > 0.0).max().orElse(0.0));
                    floats.add(floats1.stream().mapToDouble(e -> e).filter(e -> e > 0.0).min().orElse(0.0));
                    floats.add(floats1.stream().mapToDouble(e -> e).filter(e -> e > 0.0).average().orElse(0.0));
                    floats.add(floats.get() + 0.8 * (floats.get() - floats.get())); // 阈值
                }
                dataDict.put(key1, floats);
            }
            if (Arrays.equals(all_key, cpuKeys)) {
                uiPerfSummaryDo.setCpu(dataDict);
            }
            if (Arrays.equals(all_key, memKeys)) {
                uiPerfSummaryDo.setMem(dataDict);
            }
            if (Arrays.equals(all_key, powerKeys)) {
                uiPerfSummaryDo.setPower(dataDict);
            }
            if (Arrays.equals(all_key, threadKeys)) {
                uiPerfSummaryDo.setThread(dataDict);
            }
            if (Arrays.equals(all_key, pssKeys)) {
                uiPerfSummaryDo.setPss(dataDict);
            }
            if (Arrays.equals(all_key, trafficKeys)) {
                uiPerfSummaryDo.setTraffic(dataDict);
            }
        }
        uiPerfSummaryDos.add(uiPerfSummaryDo);
    }
    return uiPerfSummaryDos;
}

解析Allure报告

拿到一个用例的开始时间和结束时间,方便确定用例执行的时间范围

在13.UI自动化测试框架搭建-处理Allure报告数据中有提到如何拿到allure的内容

data = {
    "fullName": full_name,
    "status": json_data.get("status"),
    "labels": labels,
    "start": json_data.get("start", ),
    "stop": json_data.get("stop", ),
    "duration": json_data.get("stop", ) - json_data.get("start", ),
    "parameters": parameters,
    "statusDetails": statusDetails1,
    "kano_url": ",".join(kano_url),
    "steps": ";".join(steps),
    "desc": ";"
}

添加了

"start": json_data.get("start", )
"stop": json_data.get("stop", )
"desc": ";"

现在将用例执行的情况写入到一份csv文件中,方便与其他的性能数据进行比对

# 拼接csv文件路径
casesfile = os.path.join(PERF_PATH, 'cases.csv')
# csv文件头部
title = ["datetime", "flag"] + list(allure_results[].keys())
with open(casesfile, "a+", encoding="utf-8") as f:
    # 写入头部
    csv.writer(f, lineterminator='\n').writerow(title)
    for i in allure_results:
        allure_data1 = [timeoperator.get_localtime(i['start'], "%Y-%m-%d %H-%M-%S"), True] + list(
            i.values())
        allure_data2 = [timeoperator.get_localtime(i['stop'], "%Y-%m-%d %H-%M-%S"), False] + list(
            i.values())
        csv.writer(f, lineterminator='\n').writerow(allure_data1)
        csv.writer(f, lineterminator='\n').writerow(allure_data2)

使得datetime等于start和stop,写入两遍,再使用flag来标记方便后面去除掉

这样就得到一份按照时间排序的用例执行结果了

datetime,flag,fullName,status,labels,start,stop,duration,parameters,statusDetails,kano_url,steps,desc
-08-01 -04-44,True,src.cases_android.wy.test_health.TestMedicationReminder#test_close_medication_reminder,passed,微医APP_健康_用药提醒-关闭用药提醒(@钟鑫),1659333884508,1659333917256,32748,,"{'message': '', 'trace': ''}",,1-查看提醒状态;1.1-查看「用药提醒_已开启提醒」是否存在;2-关闭用药提醒;2.1-点击「用药提醒_提醒滑块1」;3-点击确认;3.1-点击「通用_确认关闭」;4-查看提醒状态;4.1-查看「用药提醒_已关闭提醒」是否存在;4.2-查看「用药提醒_已关闭提醒1」是否存在;5-打开用药提醒;5.1-点击「用药提醒_提醒滑块2」;5.2-点击「用药提醒_已关闭提醒1」;6-查看提醒状态;6.1-查看「用药提醒_已开启提醒」是否存在,;
-08-01 -05-17,False,src.cases_android.wy.test_health.TestMedicationReminder#test_close_medication_reminder,passed,微医APP_健康_用药提醒-关闭用药提醒(@钟鑫),1659333884508,1659333917256,32748,,"{'message': '', 'trace': ''}",,1-查看提醒状态;1.1-查看「用药提醒_已开启提醒」是否存在;2-关闭用药提醒;2.1-点击「用药提醒_提醒滑块1」;3-点击确认;3.1-点击「通用_确认关闭」;4-查看提醒状态;4.1-查看「用药提醒_已关闭提醒」是否存在;4.2-查看「用药提醒_已关闭提醒1」是否存在;5-打开用药提醒;5.1-点击「用药提醒_提醒滑块2」;5.2-点击「用药提醒_已关闭提醒1」;6-查看提醒状态;6.1-查看「用药提醒_已开启提醒」是否存在,;
-08-01 -24-32,True,src.cases_android.wy.test_me.TestSetting#test_entrance_text,passed,微医APP_个人中心_设置-子项入口文案检查(@钟鑫),1659331472953,1659331473076,123,'settings_message',"{'message': '', 'trace': ''}",,1-查看是否存在我_设置-消息通知;1.1-查看「我_设置-消息通知」是否存在,;
-08-01 -24-33,False,src.cases_android.wy.test_me.TestSetting#test_entrance_text,passed,微医APP_个人中心_设置-子项入口文案检查(@钟鑫),1659331472953,1659331473076,123,'settings_message',"{'message': '', 'trace': ''}",,1-查看是否存在我_设置-消息通知;1.1-查看「我_设置-消息通知」是否存在,;

数据比对

读取用例执行结果

读取文件后将它的datetimes设置为datetime类型,方便后面的排序

def cases_handle(self, path=f"{PERF_PATH}/cases.csv"):
    df = self.read_csv(path)
    df['datetime'] = pd.to_datetime(df['datetime'], format="%Y-%m-%d %H-%M-%S")
    df = df.sort_values("datetime", ascending=True)
    return df

编写比较函数

def add_desc(self, **kwargs) -> [dict]:
    ...

**kwargs动态输入键值对来进行筛选

记录原先的列

base_title = list(cases_df.columns)

先进行cpu性能数据的处理

cpu = ['device_cpu_rate%', 'user%', 'system%', 'idle%', 'pid_cpu%']

遍历输入的键值对,如果输入的key值加上%在cpu这个列表里面,那就筛选出cpu性能数据中符合条件的数据

for k, v in kwargs.items():
    if f"{k}%" in cpu:
        name = f"{k}%"
        df1 = cpu_df[cpu_df[name] > v]

将两个表合并一下

cases_df = pd.concat([df1, cases_df]).sort_values("datetime", ascending=True)

拿到超过阈值的行号索引

error_index = list(cases_df[cases_df[name] > ].index)

在超过阈值索引的附近几行添加描述

for i in error_index:
    cases_df.loc[i, 'desc'] += f"{k}超过阈值;"
    try:
        cases_df.loc[i - , 'desc'] += f"{k}超过阈值;"
    except Exception:
        pass
    try:
        cases_df.loc[i + , 'desc'] += f"{k}超过阈值;"
    except Exception:
        pass

剔除性能数据的列

cases_df = cases_df[pd.notnull(cases_df['fullName'])]

根据flag去除重复添加的用例信息

cases_df = cases_df[cases_df['flag'] == True]

根据之前保留的原始列信息将性能数据列去除

new_df = pd.DataFrame(cases_df, columns=base_title)

返回[dict]格式

return new_df.to_dict("records")

全部新增代码

def cases_handle(self, path=f"{PERF_PATH}/cases.csv"):
    df = self.read_csv(path)
    df['datetime'] = pd.to_datetime(df['datetime'], format="%Y-%m-%d %H-%M-%S")
    df = df.sort_values("datetime", ascending=True)
    return df

def add_desc(self, **kwargs) -> [dict]:
    cpu = ['device_cpu_rate%', 'user%', 'system%', 'idle%', 'pid_cpu%']
    mem = ['total_ram(MB)', 'free_ram(MB)', 'pid_pss(MB)']
    power = ['voltage(V)', 'tempreture(C)', 'current(mA)']
    traffic = ['device_total(KB)', 'device_receive(KB)',
               'device_transport(KB)',
               'pid_rx(KB)', 'pid_tx(KB)', 'pid_total(KB)']
    cpu_df = self.cpu_handle()
    mem_df = self.mem_handle()
    power_df = self.power_handle()
    thread_df = self.thread_num_handle()
    traffic_df = self.traffic_handle()
    cases_df = self.cases_handle()
    base_title = list(cases_df.columns)
    for k, v in kwargs.items():
        if f"{k}%" in cpu:
            name = f"{k}%"
            df1 = cpu_df[cpu_df[name] > v]
        if f"{k}(MB)" in mem:
            name = f"{k}(MB)"
            df1 = mem_df[mem_df[name] > v]
        if any([f"{k}({i})" in power for i in ['V', 'C', 'mA']]):
            if k == "voltage":
                name = f"{k}(V)"
                df1 = power_df[power_df[name] > v]
            if k == "tempreture":
                name = f"{k}(C)"
                df1 = power_df[power_df[name] > v]
            if k == "current":
                name = f"{k}(mA)"
                df1 = power_df[power_df[name] > v]
        if k == "threadList":
            name = "thread_num"
            df1 = thread_df[thread_df[name] > v]
        if f'{k}(KB)' in traffic:
            name = f'{k}(KB)'
            df1 = traffic_df[traffic_df[name] > v]
        cases_df = pd.concat([df1, cases_df]).sort_values("datetime", ascending=True)
        error_index = list(cases_df[cases_df[name] > ].index)
        for i in error_index:
            cases_df.loc[i, 'desc'] += f"{k}超过阈值;"
            try:
                cases_df.loc[i - , 'desc'] += f"{k}超过阈值;"
            except Exception:
                pass
            try:
                cases_df.loc[i + , 'desc'] += f"{k}超过阈值;"
            except Exception:
                pass
        cases_df = cases_df[pd.notnull(cases_df['fullName'])]
    cases_df = cases_df[cases_df['flag'] == True]
    new_df = pd.DataFrame(cases_df, columns=base_title)
    return new_df.to_dict("records")

测试

cases_data = d.add_desc(
        device_cpu_rate=, user=, system=, idle=, pid_cpu=,
        free_ram=, pid_pss=, total_ram=,
        tempreture=, voltage=,
        threadList=,
        device_transport=, device_receive=, device_total=, 
        pid_tx=, pid_total=, pid_rx=,
    )
[
...
{'datetime': Timestamp('2022-08-01 14:05:17'), 'flag': True, 'fullName': 'src.cases_android.wy.test_health.TestMedicationReminder#test_edit_medication_reminder', 'status': 'passed', 'labels': '微医APP_健康_用药提醒-修改用药提醒时间(@钟鑫)', 'start': 1659333917269.0, 'stop': 1659333926307.0, 'duration': 9038.0, 'parameters': nan, 'statusDetails': "{'message': '', 'trace': ''}", 'kano_url': nan, 'steps': '1-点击一条用药提醒记录;1.1-点击「用药提醒_其中一条用药提醒」;2-选择必要时用药;2.1-获取元素的坐标;2.2-点击坐标「558.0」「640.5」所在的位置;2.3-点击坐标「558.0」「640.5」所在的位置;2.4-点击坐标「558.0」「640.5」所在的位置;2.5-从元素「用药提醒_用药时间-每隔几天用药」滑动到元素「用药提醒_用药时间-每隔几小时用药」的位置;2.6-点击「通用_完成」;3-点击保存;3.1-点击「通用_保存」;4-查看提醒时间为必要时;4.1-转换参数化元素;4.2-查看「用药提醒_用药时间」是否存在', 'desc': ';system超过阈值;device_cpu_rate超过阈值;voltage超过阈值;voltage超过阈值;voltage超过阈值;'}
...
]
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-08-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 测试游记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 确定阈值
  • 计算各项值
    • 完整代码
    • 解析Allure报告
    • 数据比对
      • 读取用例执行结果
        • 编写比较函数
          • 全部新增代码
          • 测试
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档