基于Appium实现UI遍历工具(六)代码实现篇(下)

2022-09-29 20:00:23 浏览数 (1)

系列文章:

基于Appium实现深度UI遍历工具

基于Appium实现深度UI遍历工具(二)

基于Appium实现深度UI遍历工具(三)

基于Appium实现深度UI遍历工具(四)代码实现篇(上)

基于Appium实现UI遍历工具(五)代码实现篇(中)


经过前面的分享,我们做了功能的梳理,也对架构等进行了设计,这一篇,去聊一聊最关键的,如何实现。梳理下大概的逻辑。

代码语言:javascript复制
1.加载所有的配置
2.获取当前页面所有元素
3.形成xpath,
4.检测包名是否重启或者继续便利
5.获取activity,校验是否满足返回或者重启的需求
6.处理导航栏
7.检查当前是否有黑名单的
8.位于白名单可以多次点击
9.点击后深度增加
10.进入到自界面,判断是否满足返回等,
11.遍历子界面,
12.沿着一个路径下去遍历界面,遇到无法遍历的界面,返回上级界面
13.遍历过程中记录操作,记录性能,日志
14.汇总操作,性能日志。产出测试报告
15.执行完毕,

按照上面的思路,可以编写对应的代码。备注:性能和日志解析的暂时没有处理。

整体代码如下:放在Parsexml.py文件

代码语言:javascript复制
import difflib
import os
import time
from datetime import datetime, timedelta
from time import sleep

from selenium.common.exceptions import StaleElementReferenceException
from common.unitil import perform_back
from common.pictools import imagetovideo
from common.configuntil import *
from lxml import etree

from common.findnewfile import copy_file
from common.htmlreport import title
from common.log import LOG
import random

from common.unitil import getMobileInfo

'''
解析页面元素,并且执行
'''
class Excuption(object):
    def __init__(self, filepath, dev, path, activity):
        self.click_activity_dict = {}
        self.packname = []
        self.userLoginCount = 0
        self.currentDepth = 0
        self.parse = Parse(filepath)
        self.get_find_element_timeout = self.parse.get_find_element_timeout()
        self.runtime = self.parse.get_run_time()
        self.stop = False
        self.pressBackActivityList = self.parse.get_activity()
        self.pressBackPackageList = self.parse.get_back_packname()
        self.get_block = self.parse.get_block()
        self.get_back = self.parse.get_back()
        self.maxDepth = self.parse.get_max_deep()
        self.ignoreCrash = self.parse.get_ignor_crash()
        self.timewait = self.parse.get_find_element_wait()
        self.not_click = self.parse.get_not_click()
        self.tabbarxpath = []
        self.whitelist = self.parse.get_white()
        self.creatvideo = self.parse.vido()
        self.scoreauto = self.parse.scoreauto()
        self.scoreautonum = self.parse.autoscorecount()
        self.scorealready = 0
        self.dev = dev
        self.path = path
        self.not_link = 0
        self.activity = activity

    def run(self, deriver, starttime, andriod, apckane, curents):
        LOG.name = "UI遍历测试"
        '''
        1.获取所有的元素xpath
        2.进行遍历,如果元素是input,进行输入
        3.遍历到新的页面,在新的页面进行遍历
        4.遍历后,添加点击的xpath
        '''
        LOG.info("遍历开始")
        self.currentDepth = curents
        login = self.parse.auto_loggin()
        currntxml = deriver.page_soucre()
        if login is True:
            if (self.userLoginCount == 0):
                self.login(deriver)
            elif (self.userLoginCount < 5):
                self.login(deriver)
        self.showTabBarElement(xml_str=currntxml)
        endtime = time.time()
        if ((endtime - starttime) > self.runtime * 60):
            LOG.info("即将测试结束")
            self.stop = True

        allnode = self.xpath_list(currntxml, apckane)
        length = len(allnode) - 1
        LOG.info("当前节点的长度{}".format(str(length)))
        paege = deriver.get_wiow_size()
        width_wind = paege['width']
        heigth_wind = paege['height']
        if self.stop:
            return
        activity = deriver.current_activity()
        LOG.info("当前activity:{}".format(str(activity)))
        if len(self.pressBackActivityList) > 0:
            if activity in self.pressBackActivityList:
                deriver.take_screen(self.path)
                self.back(andriod, self.dev,width_wind,heigth_wind,True)
        packname = self.get_xml_packagename(xml_str=currntxml, andrioid=andriod)
        if apckane != packname:
            if apckane in self.pressBackPackageList:
                self.back(andriod, self.dev,width_wind,heigth_wind,True)
            else:
                # 如果当前的包名不是在返回的apk,这里应该认为崩溃
                #   整理这里的图片
                LOG.info("崩溃了,需要截图")
                deriver.take_screen(self.path)
                copy_file((datetime.now()   timedelta()).strftime("%Y_%m_%d_%H_%S_%M"), self.path)
                deriver.launch_app()
                sleep(10)
                self.run(deriver, starttime, andriod, apckane, self.currentDepth)
        self.currentDepth  = 1
        if (self.currentDepth > self.maxDepth):
            LOG.info("遍历深度过预期")
            self.stop = True
            return
        while length < 1 or self.stop is False:
            activity = deriver.current_activity()
            if activity in self.pressBackActivityList:
                self.back(andriod, self.dev, deriver, width_wind, heigth_wind, True)
            if ((endtime - starttime) > self.runtime * 60):
                self.stop = True
            try:
                xpath_all = allnode[length]
            except:
                break
            update = self.updateactivy(activity, xpath_all['xpath'])
            if self.not_link > 10:
                self.back(andriod, self.dev, deriver, width_wind, heigth_wind, True)
                self.not_link = 0
            if update is False:
                length -= 1
                allnode.remove(xpath_all)
            else:
                if len(self.get_back) > 0:
                    for key in self.get_back:
                        if str(xpath_all['xpath']).__contains__(key):
                            LOG.info(self.get_back)
                            deriver.take_screen(self.path)
                            self.back(andriod, self.dev, deriver, width_wind, heigth_wind, True)
                            allnode.remove(xpath_all)
                            break
                sleep(self.timewait)
                if xpath_all['xpath'] != "":
                    LOG.info(xpath_all['xpath'])
                    element = deriver.find_ele('xpath', xpath_all['xpath'], self.get_find_element_timeout)
                    if element is not None:
                        for key in self.parse.get_text_input():
                            if xpath_all['xpath'].__contains__(key):
                                text = self.parse.get_sendText()
                                send = random.choices(text, k=1)
                                deriver.sendkeys(element, send)
                        else:
                            try:
                                deriver.take_screen(self.path)
                                element.click()
                            except StaleElementReferenceException as e:
                                self.not_link  = 1
                            deriver.take_screen(self.path, xpath_all['bound'])
                            after = deriver.page_soucre()
                            if self.is_same_page(currntxml, after) is False:
                                self.run(deriver, starttime, andriod, apckane, self.currentDepth)
                length -= 1
                try:
                    allnode.remove(xpath_all)
                except:
                    pass
        if length < 0:
            if len(self.tabbarxpath) > 0:
                self.tabbarxpath.remove(self.tabbarxpath[0])
            if self.scoreauto and self.scorealready < self.scoreautonum:
                deriver.socrae()
                self.scorealready  = 1
                after = deriver.page_soucre()
                if self.is_same_page(currntxml, after) is False:
                    self.run(deriver, starttime, andriod, apckane, self.currentDepth)
                else:
                    if len(self.tabbarxpath) > 0:
                        weights = [1 for x in range(len(self.tabbarxpath))]
                        xpath_tar = random.choices(self.tabbarxpath, weights=weights, k=1)[0]
                        element = deriver.find_ele('xpath', xpath_tar['xpath'], self.get_find_element_timeout)
                        if element is not None:
                            element.click()
                            deriver.take_screen(self.path, xpath_tar['bound'])
                            after = deriver.page_soucre()
                            if self.is_same_page(currntxml, after) is False:
                                self.run(deriver, starttime, andriod, apckane, self.currentDepth)

    def back(self, adnriod, dev, deriver, width, height, left):

        if adnriod:
            perform_back(dev)
        else:
            startx = 0
            endx = width / 2
            if left:
                startx = width / 2
                endx = 0
            starty = height / 2
            endy = starty
            if not adnriod:
                if left:
                    startx = 0
                    endx = 750
                else:
                    startx = 750
                    endx = -750

                starty = 50
                endy = 0
            deriver.swpape(startx, starty, endx, endy)

    def updateactivy(self, activity, xpath):
        for key in self.whitelist:
            if xpath.__contains__(key):
                return True
        if activity in self.click_activity_dict.keys():
            if xpath in self.click_activity_dict[activity]:
                return False
            else:
                self.click_activity_dict[activity].append(xpath)
                return True
        else:
            self.click_activity_dict[activity] = [xpath]
            return True

    def login(self, deriver):
        login = self.parse.opearlogin()
        for item in login:
            element = deriver.find_ele('xpath', item['XPATH'], self.get_find_element_timeout)
            if item['ACTION'] == "input":
                element.clear()
                element.send_keys(item['VALUE'])
            elif item['ACTION'] == 'click':
                element.click()
            sleep(self.parse.get_find_element_wait())
        self.userLoginCount  = 1

    def xpath_list(self, xml_str, apckane):
        allxpath = []
        mytree = etree.HTML(xml_str.split("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>")[1])
        xpath = ""
        for j in mytree.xpath('//*[@package="{}"]'.format(apckane)):
            if j.attrib.get("class") not in self.not_click:
                xpath  = "//"   j.attrib.get("class")   "["
                stringbound = j.attrib.get('bounds')
                for key, value in j.attrib.items():
                    if key not in ['selected', 'bounds', 'class', 'checked', 'checkable', 'focusable', 'enabled',
                                   'long-clickable', 'scrollable', 'displayed', 'clickable', 'focused', 'password']:
                        xpath  = "@"   key   "=""   value   """   ' and '
                xpath = xpath[:-5]
                xpath  = ']'
                temp_list = {"xpath": xpath, "bound": stringbound}
                if temp_list not in self.tabbarxpath:
                    allxpath.append(temp_list)
                xpath = ""
        return allxpath

    def get_xml_packagename(self, xml_str, andrioid):
        mytree = etree.HTML(xml_str.split("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>")[1])
        if andrioid:
            appNameXpath = "(//*[@package!=''])[1]"
        else:
            appNameXpath = "//*[contains(@type,"Application")]"
        return mytree.xpath(appNameXpath)[0].get("package")

    def is_same_page(self, before, after):
        seq = difflib.SequenceMatcher(None, before, after)
        ratio = seq.quick_ratio()
        if ratio > 0.9:
            return True
        return False

    def showTabBarElement(self, xml_str):
        tabBarElemnt = self.parse.get_tar()
        mytree = etree.HTML(xml_str.split("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>")[1])
        for j in mytree.xpath('//*[%s]' % (tabBarElemnt)):
            xpath = ''
            if j.attrib.get("class") not in self.not_click:
                xpath  = "//"   j.attrib.get("class")   "["
                stringbound = j.attrib.get('bounds')
                for key, value in j.attrib.items():
                    if key != 'class' or key != 'bounds' or key != 'selected':
                        xpath  = "@"   key   "=""   value   """   ' and '
                xpath = xpath[:-5]
                xpath  = ']'
                temp_list = {"xpath": xpath, "bound": stringbound}
                self.tabbarxpath.append(temp_list)

    def createreport(self):
        model, version, newKernel, serialno, brand, sdk, rom, rom_verison = getMobileInfo(self.dev)
        self.reslut = ""
        self.reslut = ""
        run_count = 0
        for key, value in self.click_activity_dict.items():
            self.reslut  = "<p>操作的:activity {} 点击次数:{}".format(key, len(value))
            run_count  = len(value)
            self.reslut  = "</p>"
        titles = title("基于Appium UI遍历测试")
        conect = '''<div class="row " style="margin:60px">
                        <div style='    margin-top: 5%;' >
                         <table class="table table-hover table-condensed table-bordered" style="word-wrap:break-word;">
                    <tr > <td><strong>设备</strong></td><td>{}</td></tr>
                <td><strong>厂商</strong></td><td>{}</td></tr>
                   <tr > <td><strong>系统版本</strong></td><td>{}</td></tr>
                   <tr > <td><strong>测试apk</strong></td><td>{}</td></tr>
                   <tr > <td><strong>启动activty:</strong></td><td>{}</td></tr>
                   <tr >  <td><strong>测试时间:</strong></td><td>{}</td></tr>
             <tr >  <td><strong>操作次数:</strong></td><td>{}</td></tr>
                    <tr >  <td><strong>操作详情:</strong></td><td>{}</td></tr>
                        '''.format(self.dev, brand, rom,
                                   self.packname, self.activity, self.runtime, str(run_count), self.reslut)
        reslut = titles   conect
        file = self.reportfile()
        with open(file, 'a ', encoding='utf-8') as f:
            f.write(reslut)
        if self.creatvideo:
            self.video()

    def reportfile(self):
        self.repost_html = os.path.join(self.path, self.dev   ".html")
        return self.repost_html

    def video(self):
        for item in os.listdir(self.path):
            if os.path.isdir(item):
                path = os.path.join(self.path, item)
                runitem = os.path.join(self.path, item   "_crash.mp4")
                imagetovideo(path, runitem)
        run = os.path.join(self.path, self.dev   "_all.mp4")
        imagetovideo(self.path, run)

对于界面的元素的相似度,才用了对比str相似度来做的,因为这里可能刷新后,我们的界面回发生变化,上面的整体包括了用例的执行,测试报告的汇总的方法,在用例入口只需要调用对应的方法,组合调用即可。

在case目录,可以创建一个case

代码语言:javascript复制
def run(dev, apknamne, port, Testplatform, call_num,activity):
    path = os.path.join(os.path.join(os.path.join(os.getcwd(), "testreport"), call_num), dev)
    if os.path.exists(path) is False:
        os.makedirs(path)
    file = os.path.join(os.path.join(os.getcwd(), 'file'), 'config.yaml')
    LOG.name = "基于Appium UI遍历测试"
    platform_version = getversion(dev)
    starttime = time.time()
    time.sleep(10)
    derivernew = deriver_encapsulation(port,Testplatform,platform_version,dev,apknamne,activity)
    excuptionUICrawler = Excuption(file, dev, path,activity)
    excuptionUICrawler.run(derivernew, starttime, True, apknamne, 0)
    excuptionUICrawler.createreport()

在调用的地方,传递对应的参数即可。

可以看到,先梳理了整体的思路,然后去根据思路去编写对应的代码即可。

所有代码地址:

代码语言:javascript复制
https://github.com/liwanlei/appium_uicrawler

发现问题,解决问题。遇到问题,慢慢解决问题即可。

0 人点赞