Android SDK 自動(dòng)化性能測(cè)試方案: SDK + Appium + Emmagee + Python

一、前情概要

但凡接觸過(guò)性能測(cè)試的都對(duì)以下情景深有體會(huì):
?1.測(cè)試前置工作量大繁瑣,手工操作費(fèi)時(shí)費(fèi)力;
?2.測(cè)試結(jié)果有出入,自己想再確認(rèn)一下,測(cè)試過(guò)程再來(lái)一遍吧;好不容易整出了一份報(bào)告,RD一看對(duì)結(jié)果不滿意存在質(zhì)疑,來(lái)回修改幾遍整個(gè)測(cè)試過(guò)程就需要再來(lái)幾遍,整個(gè)過(guò)程下來(lái)精疲力竭;
?3.性能數(shù)據(jù)整理細(xì)致入微,測(cè)試5分鐘,數(shù)據(jù)整理2小時(shí),一頓操作下來(lái)眼睛要瞎;

二、項(xiàng)目背景

為了減少以上痛苦對(duì)身體帶來(lái)的一萬(wàn)點(diǎn)暴擊傷害,以及從整個(gè)測(cè)試模式、方法來(lái)看,急需要一套自動(dòng)化性能測(cè)試方案來(lái)提高整個(gè)性能測(cè)試的效率;同時(shí)隨著DevOps(DevOps 強(qiáng)調(diào)的是高效組織團(tuán)隊(duì)之間如何通過(guò)自動(dòng)化的工具協(xié)作和溝通來(lái)完成軟件的生命周期管理,從而更快、更頻繁地交付更穩(wěn)定的軟件)在各個(gè)一二線大廠的推行,性能測(cè)試任務(wù)接入流水線用于周期性觸發(fā)執(zhí)行也是大勢(shì)所趨,因此一套全自動(dòng)化的性能測(cè)試方案亟待推出用于解決工作的難點(diǎn)問(wèn)題,提高整個(gè)的工作效率。

三、解決的主要問(wèn)題

?1.測(cè)試過(guò)程繁瑣,人工介入成本高;
?2.測(cè)試結(jié)果有出入,自己想再確認(rèn)一下,測(cè)試過(guò)程再來(lái)一遍吧;好不容易整出了一份報(bào)告,RD一看對(duì)結(jié)果不滿意存在質(zhì)疑,來(lái)回修改幾遍整個(gè)測(cè)試過(guò)程就需要再來(lái)幾遍,整個(gè)過(guò)程下來(lái)精疲力竭;
?3.性能數(shù)據(jù)整理細(xì)致入微,測(cè)試5分鐘,數(shù)據(jù)整理2小時(shí),一頓操作下來(lái)眼睛要瞎;

四、實(shí)現(xiàn)方案

整體框架:

Python + Appium + Emmagee

實(shí)現(xiàn)原理:

Python腳本實(shí)現(xiàn)Appium對(duì)Emmagee的驅(qū)動(dòng)-》進(jìn)而調(diào)起測(cè)試APP中的測(cè)試場(chǎng)景和case-》運(yùn)行-》性能測(cè)試數(shù)據(jù)自動(dòng)讀取-》數(shù)據(jù)統(tǒng)計(jì)、分析-》輸出測(cè)試報(bào)告

實(shí)現(xiàn)場(chǎng)景:

目前已實(shí)現(xiàn)支持基礎(chǔ)地圖、個(gè)性化地圖、熱力圖、海量Polyline地圖場(chǎng)景下放大、縮小、拖拽等多種操作下的性能數(shù)據(jù)的全自動(dòng)輸出

Demo示例:
第一步:材料準(zhǔn)備

1.待測(cè)APP/待測(cè)場(chǎng)景

image.png

2.Emmage性能測(cè)試工具
3.Appium工具
4.Python 3.7環(huán)境

第二步:按步驟來(lái)實(shí)現(xiàn)每個(gè)階段的自動(dòng)化

明確要實(shí)現(xiàn)自動(dòng)化的操作步驟,如上面實(shí)現(xiàn)原理所描述的"當(dāng)準(zhǔn)備好待測(cè)APP/待測(cè)場(chǎng)景后,實(shí)現(xiàn)操作-》運(yùn)行-》數(shù)據(jù)收集-》統(tǒng)計(jì)分析-》輸出報(bào)告"的全自動(dòng)化,因此按步驟來(lái)實(shí)現(xiàn)每個(gè)階段的自動(dòng)化。
1.Python腳本實(shí)現(xiàn)Appium對(duì)Emmagee的驅(qū)動(dòng)

# performance_test_tool.py
import os
import shutil
import time
from appium import webdriver
from appium.webdriver.common.touch_action import TouchAction
from auto_analysis_tool import analysis_output_test_report

"""
入?yún)⒄f(shuō)明:
open_dir_path:Emmagee 輸出的原始性能數(shù)據(jù)文件所在目錄(文件按修改時(shí)間降序排列)
output_file_name:最終輸出的性能數(shù)據(jù)報(bào)告文件名名稱和所在路徑
start_line:截取原始性能數(shù)據(jù)文件開始行
end_line:截取原始性能數(shù)據(jù)文件結(jié)束行

"""
def start_testing(open_dir_path, output_file_name, start_line, end_line):
    # appium服務(wù)監(jiān)聽地址
    server = 'http://localhost:4723/wd/hub'

    # app啟動(dòng)參數(shù)
    desired_caps = {
        # "platformName": "Android",
        # "platformVersion": "6.0",
        # "deviceName": "T7G0215A31011599",
        # "appPackage": "com.netease.qa.emmagee",
        # "appActivity": ".activity.MainPageActivity",
        # # "appWaitActivity": ".activity.MainPageActivity",
        # # "appWaitPackage": "com.netease.qa.emmagee",
        # "appiumVersion": "2.5.1"

        "platformName": "Android",
        "platformVersion": "5.1.1",
        "deviceName": "T3Q4C16A17003811",
        "appPackage": "com.netease.qa.emmagee",
        "appActivity": ".activity.MainPageActivity"
    }
    desired_caps['unicodeKeyboard'] = True
    desired_caps['resetKeyboard'] = True

    # 驅(qū)動(dòng)
    driver = webdriver.Remote(server, desired_caps)

    # 點(diǎn)擊設(shè)置按鈕
    driver.find_element_by_id("com.netease.qa.emmagee:id/btn_set").click()

    time.sleep(2)

    # 設(shè)置采集頻率為1s
    TouchAction(driver).tap(x=97, y=449).perform()

    # 點(diǎn)擊返回按鈕
    driver.find_element_by_id("com.netease.qa.emmagee:id/go_back").click()

    time.sleep(5)

    # 獲取測(cè)試APP RadioButton
    radio_button = driver.find_element_by_xpath(
        "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.ListView/android.widget.LinearLayout[5]/android.widget.RadioButton")

    radio_button = driver.find_element_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.ListView/android.widget.LinearLayout[8]/android.widget.RadioButton")

    # 選中測(cè)試APP RadioButton
    radio_button.click()

    # 點(diǎn)擊開始測(cè)試按鈕
    driver.find_element_by_id("com.netease.qa.emmagee:id/test").click()

    # 運(yùn)行測(cè)試場(chǎng)景,如1分鐘
    time.sleep(60)

    # 點(diǎn)擊停止測(cè)試按鈕
    TouchAction(driver).tap(x=453, y=286).perform()

    # kill測(cè)試Demo
    os.system("adb shell am force-stop baidumapsdk.demo")

    time.sleep(5)

    # 清除目標(biāo)目錄下的文件
    del_file("./Emmagee/")

    # adb pull 生成的測(cè)試報(bào)告到目標(biāo)文件夾下
    os.system("adb pull /sdcard/Emmagee/ ./")

    # 解析測(cè)試報(bào)告內(nèi)容:內(nèi)存、CPU信息,并輸出分析后的測(cè)試報(bào)告
    analysis_output_test_report(open_dir_path, output_file_name, start_line, end_line)

    # 關(guān)閉會(huì)話
    driver.quit()


"""清空文件夾內(nèi)容"""


def del_file(filepath):
    """
    刪除某一目錄下的所有文件或文件夾
    :param filepath: 路徑
    :return:
    """
    if os.path.isdir(filepath):
        del_list = os.listdir(filepath)
        for f in del_list:
            file_path = os.path.join(filepath, f)
            if os.path.isfile(file_path):
                os.remove(file_path)
            elif os.path.isdir(file_path):
                shutil.rmtree(file_path)

2.性能數(shù)據(jù)收集、解析、輸出報(bào)告

'''
# auto_analysis_tool.py
自動(dòng)化工具:自動(dòng)解析原始性能報(bào)告并輸出分析后的測(cè)試報(bào)告
'''
import os
import csv
import numpy

"""
對(duì)外暴露接口:解析測(cè)試報(bào)告內(nèi)容:內(nèi)存、CPU信息,并輸出分析后的測(cè)試報(bào)告
"""


def analysis_output_test_report(open_dir_path, output_file_name, start_line, end_line):
    # 獲取原始測(cè)試報(bào)告文件路徑
    origin_file_path = get_report_path(open_dir_path)
    # 打開原始測(cè)試報(bào)告文件
    file = open(origin_file_path, encoding="gbk")
    # 讀取內(nèi)容并解析cpu/memory信息
    data = csv.reader(file)
    cpu = []
    memory = []
    for row in data:
        if data.line_num < start_line:
            continue
        # 只獲取指定count_number行
        if data.line_num > end_line:
            break
        cpu.append(float(row[5]))  # 將字符串?dāng)?shù)據(jù)轉(zhuǎn)化為浮點(diǎn)型加入到數(shù)組之中
        memory.append(float(row[2]))
        print(data.line_num, row)

    cpu_mean = numpy.mean(cpu)  # 輸出cpu均值
    cpu_max = max(cpu)  # 輸出cpu最大值

    memory_mean = numpy.mean(memory)  # 輸出memory均值
    memory_max = max(memory)  # 輸出memory最大值

    print(cpu_mean)
    print(cpu_max)

    print(memory_mean)
    print(memory_max)

    # 以約定格式輸出性能數(shù)據(jù)
    write_analysis_report(output_file_name, cpu_mean, cpu_max, memory_mean, memory_max)

    file.close()


"""
獲取測(cè)試報(bào)告文件路徑
"""


def get_report_path(open_file_path):
    dir_list = os.listdir(open_file_path)  # 列出文件夾下所有的目錄與文件
    if not dir_list:
        return
    else:
        # 注意,這里使用lambda表達(dá)式,將文件按照最后修改時(shí)間順序升序排列
        # os.path.getmtime()  函數(shù)是獲取文件最后修改時(shí)間
        # os.path.getctime() 函數(shù)是獲取文件最后創(chuàng)建時(shí)間
        dir_list = sorted(dir_list, key=lambda x: os.path.getmtime(os.path.join(open_file_path, x)), reverse=True)

        test_report_path = os.path.join(open_file_path, dir_list[0])  # 獲取Emmagee最新生成的測(cè)試報(bào)告文件
        print(test_report_path)
        return test_report_path


'''輸出分析后的測(cè)試報(bào)告'''


def write_analysis_report(output_file_name, cpu_mean, cpu_max, memory_mean, memory_max):
    with open(output_file_name, 'w', encoding="gbk") as file_object:
        file_object.write(
            "cpu_mean:" + str(cpu_mean) + "\t" + "cpu_max:" + str(cpu_max) + "\t" + "memory_mean:" + str(
                memory_mean) + "\t" + "memory_max:" + str(memory_max))

3.單個(gè)測(cè)試case運(yùn)行

# test_case1.py
from performance_test_tool import start_testing

start_testing("/Users/username/Documents/workSpace/PerformAutoTesting/Emmagee/",
             "/Users/username/Documents/workSpace/PerformAutoTesting/outputReport/case1.txt", 11,
              12)

4.性能報(bào)告文件輸出

# case1.txt
cpu_mean:17.495 cpu_max:27.59   memory_mean:89.375  memory_max:98.31

5.附件(Emmagee原始性能數(shù)據(jù)文件示例.csv)


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容