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

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

作者头像
雷子
发布2022-09-29 19:59:48
8640
发布2022-09-29 19:59:48
举报
文章被收录于专栏:雷子说测试开发

系列文章:

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

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

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

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

我们上次分享了代码实战篇上,这次分享下一些简单的封装


首先看下对于webdriver封装

代码语言:javascript
复制
import os.path
import time
from datetime import timedelta, datetime
from appium import webdriver
from common.disapp import make_dis
from common.log import LOG
from selenium.webdriver.support.wait import WebDriverWait
from appium.webdriver.common.touch_action import TouchAction
from common.pictools import opear


class deriver_encapsulation(object):
    def __init__(self, port, Testplatform, platform_version, dev, apkname, activity):

        self.port = port
        self.Testplatform = Testplatform
        self.platform_version = platform_version
        self.dev = dev
        self.apkname = apkname
        self.activity = activity
        self.driver = self.init()

    def init(self):
        dis_app = make_dis(self.Testplatform, self.platform_version, self.dev, self.apkname, self.activity)
        LOG.info(dis_app)
        deriver = webdriver.Remote('http://localhost:{}/wd/hub'.format(str(self.port)), dis_app)
        time.sleep(10)
        return deriver

    def find_ele(self, method, path, timeout=1):
        try:
            if method == 'id':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_id(path))

            elif method == 'xpath':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_xpath(path))
            elif method == 'css':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_css_selector(path))

            elif method == 'and':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_android_uiautomator
                ('new Uiselector().%s' % path))

            elif method == 'class':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_class_name
                (path))
            elif method == 'name':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_name
                (path))
            elif method == 'acces':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_accessibility_id
                (path))
            elif method == 'text':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_link_text
                (path))
            elif method == 'partial':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_partial_link_text
                (path))
            elif method == 'tag':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_element_by_tag_name
                (path))
            else:
                raise NameError('no element,please send tag,xpath,text,id,css,id,tag')
            return se
        except:
            return None

    def find_elemens(self, method, path, timeout=1):
        try:
            if method == 'id':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_elements_by_id(path))

            elif method == 'xpath':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_elements_by_xpath(path))
            elif method == 'css':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_elements_by_css_selector(path))

            elif method == 'and':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_elements_by_android_uiautomator
                ('new Uiselector().%s' % path))

            elif method == 'class':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_eelements_by_class_name
                (path))
            elif method == 'name':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_elements_by_name
                (path))
            elif method == 'acces':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_elements_by_accessibility_id
                (path))
            elif method == 'text':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_elements_by_link_text
                (path))
            elif method == 'partial':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_elements_by_partial_link_text
                (path))
            elif method == 'tag':
                se = WebDriverWait(self.driver, timeout, 0.5).until(lambda x: x.find_elements_by_tag_name
                (path))
            else:
                raise NameError('no element,please send tag,xpath,text,id,css,id,tag')
            return se
        except:
            return None

    def install(self, path):  # 安装app
        self.driver.install_app(path)

    def uninstall(self,apkname):  # 卸载app
        self.driver.remove_app(apkname)

    def instal_ios(self, bundleId):  # ios
        self.driver.remove_app(bundleId)

    def close(self):  # 关闭app
        self.driver.close_app()

    def reset(self):  # 重置app
        self.driver.reset()

    def hide_keybord(self):  # 隐藏键盘
        self.driver.hide_keyboard()

    def send_keyevent(self, event):  # 只有安卓有
        self.driver.keyevent(keycode=event)

    def sned_press_keycode(self, keycode):  # 安卓有
        self.driver.press_keycode(keycode=keycode)

    def long_press_keycode(self, keycode):  # 长按发送
        self.driver.long_press_keycode(keycode)

    def current_activity(self):
        '''
        获取当前的activity
        '''
        activity = self.driver.current_activity
        return activity

    def wait_activity(self, activity, times, interval=1):
        self.driver.wait_activity(activity, timeout=times, interval=interval)


    def run_back(self, second):
        self.driver.background_app(seconds=second)

    def is_app_installed(self, baoming):  # ios需要buildid
        self.driver.is_app_installed(baoming)

    def launch_app(self):  # 启动app
        self.driver.launch_app()

    def start_acti(self, app_package, app_activity):
        self.driver.start_activity(app_package, app_activity)

    def ios_lock(self, locktime):
        self.driver.lock(locktime)

    def yaoshouji(self):
        self.driver.shake()

    def open_tongzhi(self):  # 安卓api 18以上
        self.driver.open_notifications()

    def renturn_network(self):  # 返回网络
        network_type = self.driver.network_connection
        return network_type

    def set_network_type(self, type):
        from appium.webdriver.connectiontype import ConnectionType
        if type == 'wifi' or type == 'WIFI' or type == 'w' or type == 'WIFI_ONLY':
            self.driver.set_network_connection(ConnectionType.WIFI_ONLY)
        elif type == 'data' or type == 'DATA' or type == 'd' or type == 'DATA_ONLY':
            self.driver.set_network_connection(ConnectionType.DATA_ONLY)
        elif type == 'ALL' or type == 'all' or type == 'a' or type == 'ALL_NETWORK_ON':
            self.driver.set_network_connection(ConnectionType.ALL_NETWORK_ON)
        elif type == 'NO' or type == 'no' or type == 'n' or type == 'NO_CONNECTION':
            self.driver.set_network_connection(ConnectionType.NO_CONNECTION)
        elif type == 'AIRPLANE_MODE' or type == 'air' or type == 'ar' or type == 'fly':
            self.driver.set_network_connection(ConnectionType.AIRPLANE_MODE)
        else:
            raise NameError('plase wifi ,data,all,no,fly')

    def open_location(self):
        self.driver.toggle_location_services()

    def set_location(self, weidu, jingdu, haiba):
        self.driver.set_location(weidu, jingdu, haiba)

    def get_size(self):
        size = self.driver.get_window_size()
        return size

    def sendkeys(self, element, text):
        element.click()
        element.clear()
        element.send_keys(text)

    def screen(self, filename):
        self.driver.get_screenshot_as_file(filename)

    def closedriver(self):
        self.driver.close()

    def killdriver(self):
        self.driver.quit()

    def get_wiow_size(self):  # 获取窗口大小
        return self.driver.get_window_size()

    def swap(self, s_x, s_y, e_x, e_y):  # 从一点到另一点
        self.driver.flick(s_x, s_y, e_x, e_y)

    def swip_end(self, s_x, s_y, e_x, e_y, duration=100):
        self.driver.swipe(s_x, s_y, e_x, e_y, duration=duration)

    def toche(self, x, y, duration=500):
        self.driver.tap([(x, y)], duration=duration)

    def scroll(self, x, y):  # 滚动元素
        self.driver.scroll(x, y)

    def drag_and_drop(self, e1, e2):  # 移动元素
        self.driver.drag_and_drop(e1, e2)

    def contexts_is(self):  # 可用
        self.driver.contexts()

    def push(self, data, path):
        self.driver.push_file(data, path)

    def pull(self, path):
        self.driver.pull_file(path)

    def take_screen(self, basepath, bound=None):
        yesterday = (datetime.now() + timedelta()).strftime("%Y_%m_%d_%H_%S_%M")
        paths = os.path.join(basepath, yesterday + ".png")
        self.driver.get_screenshot_as_file(paths)
        if bound != None:
            opear(paths, bound)

    def swpape(self, startx, starty, endx, endy):
        LOG.info("scroll from : startX " + str(startx) + ", startY " + str(starty) + ", to  endX " + str(
            endx) + ",endY " + str(endy))
        self.driver.swipe(startx, starty, endx, endy, 1000)

    def socrae(self):
        '''
        滑动
        '''
        wid_hight = self.get_wiow_size()
        whidt = wid_hight['width']
        height = wid_hight['height']
        startx = whidt * 0.5
        starty = height * 0.5
        endx = startx / 2
        endy = 50
        self.swpape(startx, starty, endx, endy)

    def page_soucre(self):
        '''
        获取当前页面的布局

        '''
        return self.driver.page_source

    def click(self, x, y):
        '''
        固定坐标的点击
        '''
        t = TouchAction(self.driver)
        try:
            t.tap(x=x, y=y).wait(10).perform()
        except Exception as e:
            LOG.error("点击固定坐标异常,原因:{}".format(str(e)))

    def longcick(self, x, y):
        '''
        在某个地方长按
        '''
        t = TouchAction(self.driver)
        t.long_press(x=x, y=y).wait(10)

    def doubletap(self, x, y):
        t = TouchAction(self.driver)
        t.tap(x=x, y=y).wait(10).tap(x=x, y=y).perform()

    def draginde(self, x, y, enx, eny, andriod):
        if y < 200:
            y = 200
        t = TouchAction(self.driver)
        if andriod is False:
            enx -= x
            eny -= y
        if andriod:
            t.long_press(x=x, y=y).move_to(x=x, y=y).release().perform()
        else:
            t.long_press(x=x, y=y).wait(10).move_to(x=x, y=y).release().perform()

    def pinch(self, x, y, enx, eny, isup, height, andriod):
        '''
        设置app的放大还是缩小
        '''
        lineCenterX = (x + enx) / 2
        lineCenterY = (y + eny) / 2
        if x > enx:
            x, enx = enx, x
            y, eny = eny, y
        if y < 200:
            y = 200
        if eny < 200:
            eny = 200
        if y > height - 200:
            y = height - 200
        if eny > height - 200:
            eny = height - 200
        toochx = TouchAction(self.driver)
        touchy = TouchAction(self.driver)
        if andriod:
            if isup:
                toochx.press(x=lineCenterX, y=lineCenterY).move_to(x=x, y=y).release()
                touchy.press(x=lineCenterX, y=lineCenterY).move_to(x=enx, y=eny).release()
            else:
                toochx.press(x=x, y=y).move_to(x=lineCenterX, y=lineCenterY).release()
                touchy.press(x=enx, y=eny).move_to(x=lineCenterX, y=lineCenterY).release()
        else:
            if isup:
                toochx.press(x=lineCenterX, y=lineCenterY).wait(10).move_to(100, 0).release()
                touchy.press(x=lineCenterX, y=lineCenterY).wait(10).move_to(-100, 0).release()
            else:
                toochx.press(x=x, y=y).wait(10).move_to(x=lineCenterX - x, y=0).release()
                touchy.press(x=enx, y=eny).wait(10).move_to(x=lineCenterX - enx, y=0).release()

主要封装了一些常用的方法,方便后续的调用,增加了隐形等待,让元素定位更加不受外在的影响。

这里面有一个图像处理的方法,代码如下

代码语言:javascript
复制
import os
import cv2
from PIL import Image


def opear(image_path, bound):
    '''
    操作图片,给图片画坐标,实现点击的位置
    '''
    image = cv2.imread(image_path)
    first_point = (str(bound)[1:-1].split("][")[0])
    last_point = (str(bound)[1:-1].split("][")[1])
    c1, c2 = (int(first_point.split(",")[0]), int(first_point.split(",")[1])), \
             (int(last_point.split(",")[0]), int(last_point.split(",")[1]))
    try:
        cv2.rectangle(image, c1, c2, (0, 255, 0), 2)
        cv2.imwrite(image_path, image)
    except:
        pass
def imagetovideo(filepath, videofile):
    '''
    图片转视频
    '''
    allfile = os.listdir(filepath)
    if len(allfile)>0:
        allfile.sort(key=lambda fn: os.path.getmtime(filepath + '//' + fn))
        fps = 5 #帧率
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')  # 用于mp4格式的生成
        image = Image.open(os.path.join(filepath, allfile[0]))
        videowriter = cv2.VideoWriter(videofile, fourcc, fps, image.size)#设置
        for itemfile in allfile:

            path = os.path.join(filepath, str(itemfile))
            if os.path.isfile(path):
                frame = cv2.imread(path)
                videowriter.write(frame)
        videowriter.release()#释放
        cv2.destroyAllWindows()#销毁

整个封装呢,一个是绘制操作的区域,一个是把手机的图片进行视频化处理。

还有一个就是组成字典用于启动webdriver的

代码语言:javascript
复制
def make_dis(platformName, platformVersion, deviceName,package,activity):
    dis_app = {}
    dis_app['platformName'] = platformName
    dis_app['platformVersion'] = platformVersion
    dis_app['deviceName'] = deviceName
    dis_app['appPackage'] = package
    dis_app['appActivity'] = activity
    dis_app['androidDeviceReadyTimeout'] = TestandroidDeviceReadyTimeout
    dis_app['unicodeKeyboard'] = TestunicodeKeyboard
    dis_app['resetKeyboard'] = TestresetKeyboard
    dis_app['noReset'] = True
    return dis_app

也是很简单的,这里的配置如下

代码语言:javascript
复制
TestandroidDeviceReadyTimeout='30'
TestunicodeKeyboard=True
TestresetKeyboard=True

很简单的配置。这里配置相关的,可以在我之前的appium系列中有分享。

需要根据app获取app的一些信息

代码语言:javascript
复制
from androguard.core.bytecodes.apk import APK
#获取包名
def get_apkname(apk):
    a = APK(apk, False, "r")
    return a.get_package()
 #获取启动的activity
def get_apk_lanchactivity(apk):
    a = APK(apk, False, "r")
    return a.get_main_activity()

处理文件的。

1.找最新的文件

2.复制文件

代码语言:javascript
复制
import os
import shutil
def new_file(testdir):
    #列出目录下所有的文件
    file_list = os.listdir(testdir)
    #对文件修改时间进行升序排列
    file_list.sort(key=lambda fn:os.path.getmtime(testdir+'//'+fn))
    return file_list[-10:-1]

def copy_file(time,path):
    new_path=os.path.join(path,time)
    if os.path.exists(new_path) is False:
        os.makedirs(new_path)
    all_file=new_file(testdir=path)
    for itemfile in all_file:
        paths=os.path.join(path,itemfile)
        try:
            shutil.copyfile(paths,os.path.join(new_path,itemfile))
        except:
            pass

adb相关的封装,封装常用方法。

代码语言:javascript
复制
import platform, os
import random
import re

from androguard.core.bytecodes.apk import APK

nativeKey = "Native Heap"
dalKey = "Dalvik Heap"
totalKey = "TOTAL"
fpskey = "FPS"
net = "net"


def checkPackeExit(uid, processname: str, isAndroid: bool = False):
    if processname is None or processname == "":
        return False
    processname = processname.lower()
    grep = isgrep()
    if isAndroid:
        cmd = 'adb -s {}'.format(uid) + " shell ps | " + grep + " " + processname
        pid_line = os.popen(cmd).readlines()
        if len(pid_line.__str__().lower().split("\n")) > 2:
            return False
        else:
            pid_line = [str(i).split("\n")[0] for i in pid_line]
            for item in pid_line:
                if processname in str(item).split(" "):
                    return True
            else:
                return False


def isgrep():
    '''
    判断系统
    '''
    if platform.system().lower().__contains__("wind"):

        grep = 'findstr'
    else:
        grep = 'grep'
    return grep


def perform_home(dev):
    '''
    实现home键操作
    '''
    cmd = 'adb -s {} shell input keyevent 3'.format(dev)
    os.system(cmd)


def perform_back(dev):
    '''
    实现返回键
    '''
    cmd = 'adb -s {}  shell input keyevent 4'.format(dev)
    os.system(cmd)

def perform_audio(dev):
    '''
    调整音量
    '''

    autocmd='adb -s {}  shell input keyevent '.format(dev)
    allaudio=[autocmd+str(i) for i in range(24,26)]
    weight=[1,1]
    result = random.choices(population=allaudio, weights=weight)[0]
    os.system(result)


def retonescreen(dev):
    '''旋转屏幕'''
    randstr = 'adb -s {} shell content insert --uri content://settings/system --bind name:s:user_rotation --bind value:i:'.format(
        dev)
    addlist = [randstr + str(i) for i in range(4)]
    choice = [1, 1, 1, 1]
    result = random.choices(population=addlist, weights=choice)[0]
    os.system(result)


def getMobileInfo(serial):
    '''
    获取设备信息
    '''
    all = {
        "HUAWEI": "ro.build.version.emui",
        "vivo": "ro.vivo.os.build.display.id",
        "Xiaomi": "ro.build.version.incremental",
        "OPPO": "ro.build.version.opporom",
       
    }
    model = os.popen('adb -s %s shell getprop ro.product.model' % serial).read().replace('\n', '')
    brand = os.popen('adb -s %s shell getprop ro.product.brand' % serial).read().replace('\n', '')
    version = os.popen('adb -s %s shell getprop  ro.tools.version.release' % serial).read().replace('\n', '')
    kernel = os.popen('adb -s %s shell cat /proc/version' % serial).read()
    serialno = os.popen('adb -s %s shell getprop  ro.serialno' % serial).read().replace('\n', '')
    sdk = os.popen('adb -s %s shell getprop  ro.tools.version.sdk' % serial).read().replace('\n', '')
    try:
        newKernel = re.findall("Linux version (.*?) \(", kernel)[0] + '#' + re.findall("#(.*)", kernel)[0]
    except:
        newKernel = ''
    try:
        rom = os.popen('adb -s %s shell getprop  %s' % (serial, all[brand])).read().replace('\n', '')
    except:
        rom = os.popen('adb -s %s shell getprop  ro.tools.display.id' % serial).read().replace('\n', '')
    if (str(brand).lower() == 'xiaomi'):
        rom_verison = os.popen('adb -s %s shell "getprop | grep ro.build.version.incremental"' % serial).read().strip()
        rom_verison = (re.match('\[ro.build.version.incremental\]: \[(.*)\]', rom_verison).group(1))
    else:
        rom_verison = 'unknown'
    return model, version, newKernel, serialno, brand, sdk, rom, rom_verison


def getversion(dev):
    '''
    获取系统版本
    '''
    devverions = os.popen('adb -s {}  shell getprop ro.build.version.release'.format(dev)).readlines()
    platform_version = (devverions[0].split('\n')[0])
    return platform_version


def getactivity(filepath, apkname):
    all_list_activity = []
    apk = APK(filepath)
    elements = apk.find_tags_from_xml('AndroidManifest.xml', 'activity')
    for item in elements:
        for key, value in item.attrib.items():
            if str(key).endswith("name") and str(value).startswith(apkname):
                all_list_activity.append(value)
    return all_list_activity

对于测试报告有一个简单的封装

代码语言:javascript
复制

def title(titles):
    title = '''<!DOCTYPE html>
<html>
<head>
  <title>%s</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入 Bootstrap -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
    <!-- HTML5 Shim 和 Respond.js 用于让 IE8 支持 HTML5元素和媒体查询 -->
    <!-- 注意:如果通过 file://  引入 Respond.js 文件,则该文件无法起效果 -->
    <!--[if lt IE 9]>
     <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
     <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
    <![endif]-->
    <style type="text/css">
        .hidden-detail,.hidden-tr{
            display:none;
        }
</style>
</head>
<body>
  ''' % (titles)
    connent = '''<div  class='col-md-8 col-md-offset-4' style='margin-left: 10%;margin-top: 2%;'>
    <h1 style="text-align: center;">{}测试的结果</h1>'''.format(
        titles)
    title += connent
    return title

以上是对于一些常用的模块的封装,包括webdriver的封装。其实都是一些很简单的,封装的目的减少代码,另一个就是让层级更加明显。

这里面就是一些简单的封装,没有太多的逻辑。

下一张分享代码最后的核心的地方的编写。

所有代码地址:

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

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

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

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

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

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

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