介绍

测试报告图片

目录分析

配置 yaml 信息说明

其他说明

第一次打开 app 引导图检查例子

--- 
-
  element_info: //android.widget.Button[@text='允许']
  test_intr: 引导图
  operate_type: click
  find_type: by_xpath
  test_id: 1001
-
  element_info: cn.ibona.t1_beta:id/btn_skip
  operate_type: click
  find_type: by_id
- 
  operate_type: swipeLeft
  time: 5
  element_info: android.widget.ImageView
  find_type: class_name
- 
  element_info: android.widget.Button
  find_type: by_name
  name: 立即开启


代码分析

入口代码

入口参数化了 unittest

PATH = lambda p: os.path.abspath(
    os.path.join(os.path.dirname(__file__), p)
)
def appDevices():
    mapp = MAppDevices.getDriver()
    return BAppDevices.appDevices(mapp, PATH("../AppDevices.ini"))
ga = appDevices()
class TestInterfaceCase(unittest.TestCase):
    def __init__(self, methodName='runTest'):
        super(TestInterfaceCase, self).__init__(methodName)
    @staticmethod
    def setUpClass():
        desired_caps = {}
        global driver
        if ga.platformName == common.ANDROID or ga.platformName == common.IOS:
            if common.FLAG:
                desired_caps['platformName'] = ga.platformName
                desired_caps['platformVersion'] = ga.platformVersion
                desired_caps['deviceName'] = ga.deviceName
                desired_caps['appPackage'] = ga.appPackage
                desired_caps['appActivity'] = ga.appActivity
                desired_caps['app'] = PATH(
                '../img/t.apk'
            )
            #     desired_caps["unicodeKeyboard"] = "True"
            #     desired_caps["resetKeyboard"] = "True"
                common.PACKAGE = ga.appPackage
                driver = webdriver.Remote(ga.Remote, desired_caps)
                common.DRIVER = driver
                common.FLAG = False
    def setUp(self):
        print("setUp")
    @staticmethod
    def tearDownClass():
        # driver.close_app()
        # driver.quit()
        print('tearDownClass')
    @staticmethod
    def parametrize(testcase_klass):
        testloader = unittest.TestLoader()
        testnames = testloader.getTestCaseNames(testcase_klass)
        suite = unittest.TestSuite()
        for name in testnames:
            suite.addTest(testcase_klass(name))
        return suite

# 引用需要导入的class写的case
from testCase.Home import testHome  
from testCase.work import testContact

def runnerCaseApp():

    start_test_time = dataToString.getStrTime(time.localtime(), "%Y-%m-%d %H:%M %p")
    suite = unittest.TestSuite()
    starttime = datetime.datetime.now()
    suite.addTest(TestInterfaceCase.parametrize(testHome))
    suite.addTest(TestInterfaceCase.parametrize(testContact))
    unittest.TextTestRunner(verbosity=2).run(suite)
    endtime = datetime.datetime.now()
    get_common_report(start_test_time, endtime, starttime)
    report()



if __name__ == '__main__':
    if BAdbCommon.attached_devices():
        if ga.platformName == common.ANDROID or ga.platformName == common.IOS:
            appium_server = BtestServer.AppiumServer(ga.appiumJs, ga.Remote)
            appium_server.start_server()
            while not appium_server.is_runnnig():
                time.sleep(2)
            runnerCaseApp()
            appium_server.stop_server()

    else:
        print(u"设备不存在")

看看 class 怎么读取 yaml 配置文件的 case

class testHome(TestInterfaceCase):
    def __init__(self, methodName=''):
        super(testHome, self).__init__(methodName)
        self.bc = BaseCaseList.BexceCase(test_module="个人中心", getTempCase=MBaseTestCase.getTempCase, BaseTestCase=MBaseTestCase.BaseTestCase, fps=[], cpu=[], men=[])
    def home_fist_open(self):
        self.bc.execCase(PATH("yaml/myinfo/home_fist_open.yaml"), test_name="test_home_fist_open", isLast="0")

    def home_login(self):
        self.bc.execCase(PATH("yaml/myinfo/home_login.yaml"), test_name="test_home_login", isLast="0")

    def home_feed(self):
        self.bc.execCase((PATH("yaml/myinfo/home_feed.yaml")), test_name="test_home_feed", isLast="1") # isLast表示的意思是最后一个case用于后面的统计
    @staticmethod
    def tearDownClass():
        pass
   # 注意这里的小技巧,这样按顺序放入case,可以按顺序执行
    def test_home(self):
        self.home_fist_open()
        self.home_login()
        self.home_feed()

execCase 是所有用例 class 都必须用的,读取 case 和调用公告方法去执行所有的 case,调用监控方法等

class BexceCase():

    def __init__(self, test_module="", getTempCase="", BaseTestCase="",fps=[], cpu=[], men=[]):
        self.test_module = test_module
        self.getTempCase = getTempCase
        self.BaseTestCase = BaseTestCase
        self.fps = fps
        self.cpu = cpu
        self.men = men
    def getModeList(self, f):
        bs = []
        gh = getXMl.getYam(f)
        for i in range(len(gh)):
            if i == 0:
                  #用例id
                self.getTempCase.test_id = gh[i].get("test_id", "false")
                 # 用例介绍
                self.getTempCase.test_intr = gh[i].get("test_intr", "false")
            # bt = self.BaseTestCase
            self.BaseTestCase.element_info = gh[i].get("element_info", "false")

          # 操作类型
            self.BaseTestCase.operate_type = gh[i].get("operate_type", "false")
            # 输入文字
            self.BaseTestCase.name = gh[i].get("name", "false")

            self.BaseTestCase.index = gh[i].get("index", "false")

            self.BaseTestCase.text = gh[i].get("text", "false") # 对应by_link_text

           # 验证类型
            self.BaseTestCase.find_type = gh[i].get("find_type", "false")

            self.BaseTestCase.time = gh[i].get("time", 0)

            bs.append(json.loads(json.dumps(self.BaseTestCase().to_primitive())))
        return bs

    def execCase(self, f, **kwargs):
        logTest = testLog.myLog().getLog()
        bc = self.getModeList(f)
        go = bo.getOperateElement(driver=common.DRIVER)
        ch_check = bc[-1]
        for k in bc:
            if k["operate_type"] != "false":
                if go.operate_element(k)== False:
                     logTest.checkPointNG(common.DRIVER, kwargs["test_name"], kwargs["test_name"])
                     logTest.resultNG(kwargs["test_name"], "找不页面元素")

                get_men = ap.get_men(common.PACKAGE)
                get_cpu = ap.top_cpu(common.PACKAGE)
                get_fps = ap.get_fps(common.PACKAGE)


                self.cpu.append(get_cpu)
                self.men.append(get_men)
                self.fps.append(get_fps)

                common.MEN.append(get_men)
                common.CPU.append(get_cpu)
                common.FPS.append(get_fps)


        if go.findElement(ch_check):
            common.test_success += 1
            self.getTempCase.test_result = "成功"
            logTest.resultOK(kwargs["test_name"])
        else:
            # logTest.screenshotNG(common.DRIVER, kwargs["test_name"])
            logTest.checkPointNG(common.DRIVER, kwargs["test_name"], kwargs["test_name"])
            common.test_failed += 1
            test_reason = "检查不到元素"
            # if common.I_ANR > 0:
            #     test_reason = "有ANR错误"
            # if common.I_CRASH > 0:
            #     test_reason = "有CRASH错误"
            # if common.I_EXCEPTION > 0:
            #     test_reason = "有EXCEPTION错误"
            self.getTempCase.test_result = "失败"
            self.getTempCase.test_reason = test_reason

        self.getTempCase.test_name =kwargs["test_name"]
        self.getTempCase.test_module = self.test_module
        common.test_sum += 1

        self.getTempCase.test_men_max = rp.phone_max_use_raw(self.men)
        avg_men = ba.get_avg_raw(self.men)  # 获取每次占用内存多少
        self.getTempCase.test_men_avg = avg_men
        self.getTempCase.test_cpu_max = rp.phone_avg_max_use_cpu(self.cpu)
        self.getTempCase.test_cpu_avg = rp.phone_avg_use_cpu(self.cpu)
        self.getTempCase.test_fps_max = rp.fps_max(self.fps)
        self.getTempCase.test_fps_avg = rp.fps_avg(self.fps)

        common.RESULT["info"].append(json.loads(json.dumps(self.getTempCase().to_primitive())))
        if kwargs["isLast"] == "1":
        # 最后case要写最下面的统计步骤
            common.RRPORT["info"].append(common.RESULT["info"])

上面说的是 BexceCase 获取 case 后,主要调用的是 getOperateElement 里面的查找元素和执行操作

# 此脚本主要用于查找元素是否存在,操作页面元素
class getOperateElement():
    def __init__(self, driver=""):
        self.cts = driver
    def findElement(self, mOperate):
        '''
        查找元素.mOperate是字典
        operate_type:对应的操作
        element_info:元素详情
        find_type: find类型
        '''
        try:
            WebDriverWait(self.cts, common.WAIT_TIME).until(lambda x: elements_by(mOperate, self.cts))
            return True
        except selenium.common.exceptions.TimeoutException:
            return False
        except selenium.common.exceptions.NoSuchElementException:
            print("找不到数据")
            return False


    def operate_element(self,  mOperate):
        if self.findElement(mOperate):
            elements = {
                common.CLICK: lambda: operate_click(mOperate, self.cts),
                # common.TAP: lambda: operate_tap(mOperate["find_type"], self.cts,  mOperate["element_info"], arg),
                common.SEND_KEYS: lambda: send_keys(mOperate, self.cts),
                common.SWIPELEFT: lambda : opreate_swipe_left(mOperate, self.cts)
            }
            return elements[mOperate["operate_type"]]()
        return False

# 点击事件
def operate_click(mOperate,cts):
    if mOperate["find_type"] == common.find_element_by_id or mOperate["find_type"] == common.find_element_by_name or mOperate["find_type"] == common.find_element_by_xpath:
        elements_by(mOperate, cts).click()
    if mOperate["find_type"] == common.find_elements_by_id or mOperate["find_type"] == common.find_elements_by_name:
        elements_by(mOperate, cts)[mOperate["index"]].click()
    # 记录运行过程中的一些系统日志,比如闪退会造成自动化测试停止
    errorLog.get_error(log=r"d:\operate_log.txt")

def opreate_swipe_left(mOperate, cts):
    time.sleep(1)
    width = cts.get_window_size()["width"]
    height = cts.get_window_size()["height"]
    for i in range(mOperate["time"]):
        cts.swipe(width/4*3, height / 2, width / 4 *1, height / 2, 500)
        time.sleep(1)
# start_x,start_y,end_x,end_y

# 轻打x轴向右移动x单位,y轴向下移动y单位
# def operate_tap(elemen_by,cts,element_info, xy=[]):
#     elements_by(elemen_by, cts, element_info).tap(x=xy[0], y=xy[1])

def send_keys(mOperate,cts):
    elements_by(mOperate, cts).send_keys(mOperate["text"])


# 封装常用的标签
def elements_by(mOperate, cts):
    elements = {

        common.find_element_by_id : lambda :cts.find_element_by_id(mOperate["element_info"]),
        common.find_elements_by_id : lambda :cts.find_elements_by_id(mOperate["element_info"]),
        common.find_element_by_xpath: lambda :cts.find_element_by_xpath(mOperate["element_info"]),
        common.find_element_by_name: lambda :cts.find_element_by_name(mOperate['name']),
        common.find_elements_by_name: lambda :cts.find_elements_by_name(mOperate['name'])[mOperate['index']],
        common.find_element_by_class_name: lambda :cts.find_element_by_class_name(mOperate['element_info']),
        common.find_elements_by_class_name: lambda :cts.find_elements_by_class_name(mOperate['element_info'])[mOperate['index']]
    }
    return elements[mOperate["find_type"]]()

最后也贴下怎么监控 men,cpu,fps

# 常用的性能监控
def top_cpu(pkg_name):
    result = 0
    cmd = "adb shell dumpsys cpuinfo | grep -w " + pkg_name+":"
    temp = []
    # cmd = "adb shell top -n %s -s cpu | grep %s$" %(str(times), pkg_name)
    top_info = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.readlines()
    for info in top_info:
        temp.append(info.split()[2].decode()) # bytes转换为string
        break
    for i in temp:
        if i != "0%":
            print("cpu="+i)
            result = int(i.split("%")[0])
    return result

# 得到men的使用情况
def get_men(pkg_name):
    result = "0"
    cmd = "adb shell  dumpsys  meminfo %s"  %(pkg_name)
    temp = []
    m = []
    men_s = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.readlines()
    for info in men_s:
        temp.append(info.split())
    m.append(temp)
    for t in m:
        result = t[19][1]
        break
    return int(result.decode())

# 得到fps
def get_fps(pkg_name):
    _adb = "adb shell dumpsys gfxinfo %s | grep -A 128 'Execute'  | grep -v '[a-Z]' "%pkg_name
    result = os.popen(_adb).read().strip()
    result = result.split('\r\n')
    # r_result = [] # 总值
    # t_result = [] # draw,Process,Execute分别的值
    # f_sum = 0
    for i in result:
        l_result = i.split('\t')[-3:]
        f_sum = 0
        for j in l_result:
            r = re.search(r"\d+\.\d+", str(j))
            if r:
                f_sum += float(r.group())
            # t_result.append('%.2f'%f_sum)
        return float('%.2f'%f_sum)

结束语


↙↙↙阅读原文可查看相关链接,并与作者交流