APP自动化

环境搭建

所需环境:jdk 、android-sdk、appium、模拟器

1、Jdk安装

说明:安卓应用或开发工具是使用java语言开发,必须使用jdk。

2、android-sdk

  说明:android开发工具包

  安装: 1、解压到指定目录 2、将目录添加到path中

3、appium安装

  说明: 需要安装appium服务端程序和python中调用的api库

  服务端:

    作用:将脚本发送给手机

    安装:双击安装程序 appium-desktop-setup-1.8.0.exe ,一直到完成即可。

  python的appium. api库

    作用:自动化测试使用api

    安装: pip install Appium-Python-Client==1.2.0

4、模拟器

  说明:模拟安卓手机

  安装: 雷电、mumu、夜神,默认安装完成即可

常用adb命令

说明:通过电脑,操作android系统的工具。

 

adb connect 设备号#连接手机 adb device#查看连接的设备 device连接成功 adb kill-server#关闭当前启动的adb adb start-server#重新启动adb adb -s 设备号 shell getprop ro.build.version.release#获取系统版本 adb push 本地路径\文件或文件夹 /手机端路径 #把本地(pc机)的文件或文件夹复制到设备(手机) adb pull 手机端路径/文件或文件夹 pc机路径\ #把设备(手机)的文件或文件夹复制到本地 adb logcat #查看日志 adb shell#登录设备 shell,该命令将登录设备的shell(内核), 登录shell后,可以使用 cd,ls,rm ,exit等Linux命令 adb shell dumpsys window windows | findstr mFocusedApp#获取到设备当前包名和启动页名 adb shell monkey -p 软件包名 -vvv 1#获取到设备当前包名cmp和启动页名launch adb install 文件路径\apk#安装apk adb uninstall 包名#卸载该软件/app adb shell am start -W [包名]#启动APP,查询App的启动时间 adb shell am start -n [包名]/[包名.Activity]#启动一个Activity(不记录启动时间) adb shell am force-stop [包名]#关闭一个APP进程

py代码操作设备

前置:必须启动appium服务、模拟器。

 

from appium import webdriver # 定义字典变量 desired_caps = {} # 字典追加启动参数 desired_caps["platformName"] = "Android" #设备系统名 # 注意:版本号必须正确 desired_caps["platformVersion"] = "7.1.1" #版本号 # android不检测内容,但是不能为空 desired_caps["deviceName"] = "192.168.56.101:5555" #adb device连接名 desired_caps["appPackage"] = "com.android.settings" #启动APP包名 desired_caps["appActivity"] = ".Settings" #启动APP启动页 # 设置中⽂ desired_caps["nicodeKeyboard"] = True desired_caps["resetKeyboard"] = True # 保留缓存信息,不自动清除数据 desired_caps["noRest"] = True # 获取driver #类似web测试浏览器驱动 #连接appium服务端的地址和打开appium服务端的端口/wd/hub desired_caps 配置的追加启动参数 driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps) #启动应⽤ driver.start_activity(包名,启动名) #appium⽀持跨应⽤,可以在操作应⽤中切换到B应⽤。 driver.current_package#获取当前应⽤包名 driver.current_activity#获取当前应⽤启动名 driver.start_activity(appPackage, appActivity)#跳转应用 driver.current_package#获取包名 driver.current_activity#获取界面名 driver.close_app()#关闭APP driver.quit()#关闭驱动 driver.install_app(app_path)#安装app driver.remove_app(app_id)#卸载app driver.is_app_installed(app_id)#判断app是否安装 driver.background_app(seconds)#置于后台 driver.push_file("/sdcard/1.png",source_path=r"E:\习知\截图\1.png")#电脑上传文件到手机 bs64 = driver.pull_file("/sdcard/1.png")#从手机下载文件 base64编码格式 c = str(base64.b64decode(bs64))#base64编码格式转为字符串 with open(r"E:\习知\截图\1.png" , "wb") as f: f.write(c.encode()#保存文件字符串转为二进制 print(driver.page_source)#获取本页源代码 driver.make_gsm_call("电话号",GsmCallActions.CALL)#模拟来电话 driver.send_sms("电话号","短信内容")#模拟来短信 driver.set_network_connection(4)#设置网络模式0全都关1飞行模式2WiFi模式4数据模式6WiFi和数据模式 driver.get.screenshot_as_png(self)#截图 driver.get_window_size()#获取屏幕宽高

appium界面

appium Inspector详细介绍

Inspector界面图标说明:

界面上从做到右图标如下:

详细说明如下:

  图标 名称 说明

  1 Show Element Handles 是否显示元素句柄

  2 Select Elements 选择元素定位

  3 Tap/Swipe By Coordinates 按坐标点击/滑动

  4 Download Screenshot 下载屏幕截图

  5 Press Back Button 按下后退按钮

  6 Press Home Button 按主页按钮

  7 Press App Switch Button 按应用程序切换按钮

  8 Native App Mode 本机应用程序模式

  9 Web/Hybrid App Mode Web/混合应用程序模式

  10 Refresh Source & Screenshot 刷新源和屏幕截图

  11 Search for element 搜索元素,校验路径与语法是否一致

  12 Start Recording 开始录制,操作宏生产代码

  13 Quit Session & Close Inspector 退出会话并关闭检查器

元素定位

通过AppiumBy进行元素定位,方式与selenium一致

 

#AppiumBy独有的定位方式ANDROID_UIAUTOMATOR使用安卓开发语言 driver.find_element(AppiumBy .ANDROID_UIAUTOMATOR,'new UiScrollable(new UiSelector().scrollable(true)' '.instance(0)).scrollIntoView(new UiSelector()''.text("查找文本").instance(0));') #滑动着定位元素,找【查找文本】这个元素

等待

显示等待,隐式等待写法用法方式与selenium一致

显示等待

  webDriverwait(driver,timeout,po11_frequency=0.5,ignored_exceptions=None)

  WebDriverWait类是由WebDirver提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常。

隐式等待

  driver.implicitly_wait(n)

  隐式等待是根据事件进行等待,等待特定时间。n的单位为秒,n为最大值,在这个最大值内只要元素定位到就结束等待

对元素操作

基本元素操作

  1、click( ):点击元素

  2、send_keys(" "):在元素中输入文本内容

  3、clear( ):在元素中清除文本

获取元素属性操作

  1、text:获取元素属性中的text值

  2、tag_name:获取元素的标签名(原生应用无标签名,所以为None)

  3、get_attribute(self,*name):根据属性名获取元素属性,属性名填写错误会报错

  4、size:获取元素的宽和高,返回字典{'height':高,'width':宽}

  5、location:获取元素左上角的坐标,返回字典{'x':坐标,'y':坐标}

  6、rect:元素的大小和位置组合字典,返回字典{'height':高,'width':宽,'x':坐标,'y':坐标}

元素判断的操作

  1、is_displayed(self):此元素是否可见。隐藏元素和被控件挡住无法操作的元素(仅限H5和webview支持)返回布尔值

  2、is_enabled(self):此元素是否可用。元素灰色和无法操作的元素(仅限H5和webview支持)返回布尔值

  3、is_selected(self):此元素是否被选中。适用于单选框、复选框等类型的元素(仅限H5和webview支持)返回布尔值

滑动操作

  swipe ()方法

    从一个点滑动到另一个点,可选择持续时间,具有滑动的惯性。需要的参数如下:

 

driver.swipe(start_x=weight * 0.5,#开始坐标 x start_y=height * 0.8,#开始坐标 y end_x=weight * 0.5,#结束坐标 x end_y=height * 0.2,#结束坐标 y duration=2000)#滑动持续的时间,默认为 0。(可选)

  scroll() 方法

    从一个元素滑动到另一个元素,具有滑动的惯性。需要的参数如下:

 

driver.scroll(image_btn, button, duration=2000) origin_el:滑动的起始元素。 destination_el:滑动的结束元素

拖拽操作

  drag拖拽事件

  从⼀个元素滑动到另⼀个元素,第⼆个元素替代第⼀个元素原本屏幕上的位置。

  drag_and_drop()方法说明, 参数:

 

driver.drag_and_drop(origin_el, destination_el) origin_el:滑动开始的元素 destination_el:滑动结束的元素

切换后台

 

odriver.background_app(seconds)#置于后台seconds几秒

模拟手势 destination_el:滑动结束的元素

  按压——TouchAction包

    使用press()函数可以模拟手指在特定控件或区域进行按压的动作

 

#使用前需要导入appium.webdriver.common.touch_action下的TouchAction包【【快不用了】】 t_driver.press(x=120, y=120).wait(1000).move_to(x,y)release() #以上代码相当于模拟按住指定坐标的元素后停顿1秒,移动到x,y再放开的操作 t_driver.perform() #立即执行操作

   w3c action双指模拟放大

    1、使用 add_point_input 添加手指,放大需要用到两个手指。

    2、使用 create_pointer_move 移动到屏幕的正中间

    3、使用 create_pointer_down 按下手指

    4、使用 create_pointer_move 往屏幕相反方向移动

    5、使用 create_pointer_up 松开手指

    create_pointer_pause()手指停留几秒

 

#导入类库 from selenium.webdriver import ActionChains def zoom( driver: Remote) : #放大操作 actions = ActionChains(driver) actions.w3c_actions.devices = [ ] #创建两根手指 finger1 = actions.w3c_actions.add_pointer_input( 'touch', 'finger1' ) finger2 = actions.w3c_actions.add_pointer_input( 'touch', 'finger2 ') #获取屏幕中位置 width = driver.get_window_size() ['width'] height = driver.get_window_size()['height'] #两个手指移动到屏幕正中间 finger1.create_pointer_move(x=width * 0.5,y=height * 0.5) finger2.create_pointer_move(x=width * 0.5,y=height * 0.5) #两个手指按下去 finger1.create_pointer_down(MouseButton.LEFT) finger2.create_pointer_down(MouseButton.LEFT) #两个手指移动 finger1.create_pointer_move(x=width * 0.5,y=height * 0.9) finger2.create_pointer_move(x=width * 0.5,y=height * 0.1) #两个手指松开 finger1.create_pointer_up(MouseButton.LEFT)#MouseButton.LEFT默认固定参数 finger2.create_pointer_up(MouseButton.LEFT) actions.perform()#立刻执行

APP框架封装

企业微信为例

  toast框,弹出框定位,固定写法

 

#第一种toast查找 driver.find_element(MobileBy.XPATH,'//*[@class="android.widqet.Toast"]').text #第二种toast查找 self.driver.find_element(MobileBy.XPATH,'//*[contains(@text,"Clicked popup")]').text

  base/base.py

 

from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy from base.handle import warpper class Base: def __init__(self,driver=None): if driver is None: caps ={#连接设备软件打开 "platformName" : "Android", "platformVersion": "6.0.1", "deviceName" : "127.0.0.1:7555", "appPackage" : "com.tencent.wework", "appActivity" : ".launch.LaunchSplashActivity", "unicodeKeyboard" : True, "resetKeyboard": True , "noReset": True } self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", caps) self.driver.implicitly_wait(10) else: self.driver.WebDriver= driver #实例化日志 self.log = Log().log() #封装一些方法 @warpper#错误截图 def find(self, by,loc=None): self.log.info( "find: ") self.log.info(f"查找方式:{by},查找元素:{loc}") return self.driver.find_element(by, loc) @warpper#错误截图 def finds(self, by, loc): self.log.info( "find: ") self.log.info(f"查找方式:{by},查找元素:{loc}") return self.driver.find_elements(by,loc) def click(self, by=None,loc=None,text=None,num=3,flag=False): if flag: ele = self.swipe.find(text,num) else: ele = self.find(by,loc) self.log.info( "find and click: ") self.log.info(f"查找方式:{by},查找元素:{loc},点击元素:{loc}") ele.click() def send(self, by,loc,text): ele = self.find(by,loc) ele.clear() self.log.info( "find and input: ") self.log.info(f"查找方式:{by},查找元素:{loc},元素输入内容:{text}") ele.send_keys(text) def text(self,by,loc): self.log.info("get text: ") res = self.find(by, loc).text self.log.info(f"查找方式.{by},查找元素:{loc},获取元素文本内容:{res}") return res def toast(self):#弹出框定位方法 return self.text(AppiumBy.XPATH,'//*[@class="android.widget.Toast"]') #滑动查找方法 def swipe_find(self, text,num=3): self.log.info("swipe find: ") self.log.info(f"获取内容:{text}") self.log.info(f"滑动查找的次数:{num}") for i in range(num): try: print(text,num) ele = self.find_element(AppiumBy.XPATH,f"//*[@text='{text}']") return ele except: self.log.info("没找元素,滑动屏幕") size = self.driver.get_window_size() width = size.get('width') height = size.get("height") start_x = width/2 end_x = start_x start_y = height * 0.8 end_y = height * 0.4 duration = 500 self.driver.swipe(start_x, starty, end_x,end_y, duration) if i a== num - 1: raise Exception(f"找了{num}次,未找到元素")

  base/handle.py错误截图方法

 

import allure def warpper(fun): def inner(*args,**kwargs) : from base.base import Base s:Base = args[0] try: return fun(*args,**kwargs) except: allure.attach(s.driver.get_screenshot_as_png(),attachment_type=allure.attachment.type.PNG) return inner

  page/address_list_page.py

 

from selenium.webdriver.common.by import By from base.base import Base from page.member_list_page import MemberListPage class AddressListPage(Base): def edit_member(self): self.click(text="添加成员",flag=True) return MemberListPage(self.driver)

  page/index_page.py

 

from selenium.webdriver.common.by import By from base.base import Base from page.address_list_page import AddressListPage class IndexPaqe(Base): __add_member_btn = (AppiumBy.XPATH,'//*[@text="通讯录"]') def goto_address_list(self): self.click(*self.__add_member_btn) return AddressListPage(self.driver)

  page/member_list_page.py

 

from selenium.webdriver.common.by import By from base.base import Base class MemberListPage(Base): __phones_list = (By.XPATH,'//*[@text="手动输入添加"]') def get_result(self): self.click (*self.__phones_list) return AddMemberPage(self.driver) def get_toast(self): res=self.toast() return res

  page/add_member_page.py

 

from appium.webdriver.common.appiumby import AppiumBy from base.base import Base class AddMemberPage(Base): __input = (AppiumBy.XPATH,"//*[@text='必填']") __save_btn = (AppiumBy.XPATH,"//*[@text='保存']") def edit_member_info(self, name,phone): self.send(*self.__input,text=name) self.send(*self.__input,text=phone) self.click(*self.__save_btn) from page.member_list_page import MemberListPage return MemberListPage(self.driver)

  cases/add_member_test.py

 

import random from page.index_page import IndexPage class TestAddMember: def setup_class(self): self.index = IndexPage() def test_01(self): name =f"ttt{random.randint(0,999999)}" phone = f"13777{random.randint(108800,999999)}" res = self. index.goto_address_list().edit_member().goto_add_member().editmmenber_info(name,phone).get_toast() assert res =="添加成功"

  完善 日志utils/log_util.py

 

import logging import os class Log: def log(self): """ 日志封装 """ #创建日志器 log = logging.getLogger('log') if not log.handlers:#这里进行判断,如果Logger .handlers列表为空,则添加,否则,直接去写日志,可以避免重复打印日志 #设置日志等级 log.setLevel(logging.INFO) #创建控制台处理器,设置日志信息要输出到哪 sh = logging.StreamHandler() #判断文件夹是否存在,不存在则创建 if not os.path.lexists(os.path.dirname(os.path.dinname(__file__))+ '/log/logs.log'): os.mkdir(os.path.dirname(os.path.dirname(__file__))+ '/log') #创建文本处理器,将日志内容保存到文件 fh = logging.FileHandler(os.path.dirname(os.path.dirname(__file__))+'/log/logs.log',encodings 'utf-8') #把日志信息放到控制台 log.addHandler(sh) log.addHandler(fh) #创建格式器,决定日志的显示格式 #定义日志输出内容(fmt). #日志常规包含。时间文件名行日志等级事件描述 #输出时间格式,s是字符串。%(asctime)s #输出文件名,字符串格式,输出行号,整数格式。[Xfilename]s:%(lineno)d #输出的日志等级:[%(leveLname)s] #输出类名,X(module)s#输出方法名,%(funcName)s#格式化日期(datefmt) fm="%(asctime)s [%(filename)s]:%(lineno)d [%(module)s] %(funcName)s %(levelname)s : %(message)s" formater = logging.Formatter(fat=fm,datefmt="%Y-%m-%d %X") #一个日志器可以有多个处理器,并且每个处理器可以有各自的格式器及过波器 #优化显示日志信息控制台的日志内容格式 sh.setFormatter(formater) #文本处理器内容格式 fh.setFormatter(formater) return log

装饰器实现黑名单功能

定位失败时找一下有没有黑名单内容有干掉继续执行没有就报错

  • 30
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值