前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于Appium实现UI遍历工具(六)代码实现篇(下)

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

作者头像
雷子
发布2022-09-29 20:00:23
5900
发布2022-09-29 20:00:23
举报
文章被收录于专栏:雷子说测试开发

系列文章:

基于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

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 雷子说测试开发 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档