通过adb与python结合创建的设备驱动脚本deviceDriver.py

来源:互联网 发布:mac aecc2017切换中文 编辑:程序博客网 时间:2024/05/23 11:06
#coding=utf8'''adb shell input关键命令如下:输入文本信息:adb shell input text   <string> 例如:input text  hello按键操作:adb shell input keyevent  [--longpress]  <key code or name>例如:input keyevent  1 或者 input keyevent  KEYCODE_MENU点击操作:adb shell input tap <x> <y>例如:input tap 100 200滑动操作:adb shell input swipe <x1>  <y1>  <x2>   <y2>例如:input swipe 0 0  100  100 截屏:adb shell screencap <filename>例如:adb shell screencap /sdcard/screen.png'''#用于获取设备临时文件存放目录import tempfile  #调用系统命令popenimport os  import re  import time  import xml.etree.cElementTree as ET  class DeviceDriver(object):      """     通过元素定位,需要Android 4.0以上     """      def __init__(self):          """         初始化,创建一个设备对象,以及获取当前PC下临时文件的路径;        调用re库,创建一个以数字的模式对象。        """          #获取当前设备临时文件存放的目录        self.tempFile = tempfile.gettempdir()         #创建一个以数字匹配的规则对象        self.pattern = re.compile(r"\d+")         try:            #创建设备连接的对象实例            self.devices=os.popen("adb wait-for-device ")        except Exception:            print u"连接设备....."        finally:            self.devices.close()                         #获取活动页面的UI信息文件    def __uidump(self):          """         获取设备当前活动页面的hierarchy的UI信息,        通过uiautomator dump命令把UI信息存放在uidump.xml文件中;        通过adb pull命令把uidump.xml文件下载到本地临时文件目录下                """          try:            if self.devices:                #获取设备当前活动页面的hierarchy的UI信息,                #通过uiautomator dump命令把UI信息存放在uidump.xml文件中;                dumpui=os.popen("adb shell uiautomator dump --compressed  /data/local/tmp/uidump.xml")                  #通过adb pull命令把uidump.xml文件下载到本地临时文件目录下                pulldumpfile=os.popen("adb pull /data/local/tmp/uidump.xml   " + self.tempFile)             else:                print u"查看设备是否连接....."        except Exception:            print "Get uidump.xml Fail...."        finally:            #关闭连接            pulldumpfile.close()            dumpui.close()                                     #获取元素的位置信息    def __element(self, attrib, name):          """         同属性单个元素,返回单个坐标元组         """                 #调用函数,把uidump.xml文件存放在本地临时目录下        self.__uidump()         try:             #把uidump.xml文件转换成元素tree            tree = ET.ElementTree(file=self.tempFile + "\\uidump.xml")              #使用iter方法对tree中标记为node进行深度遍历            #把node元素存放在treeIter中            treeIter = tree.iter(tag="node")              #对treeIter总元素进行处理            for elem in treeIter:                  #进行元素属性批判的                #如果属性值相同执行下面命令                if elem.attrib[attrib] == name:                      #获取满足要求元素的bounds值                    bounds = elem.attrib["bounds"]                      #获取页面元素是否可以点击属性                    clickable=elem.attrib["clickable"]                     #通过数字匹配规则,把bouns合并成一个list                    coord = self.pattern.findall(bounds)                     #获取元素的X坐标                    #通过把元素的左右边界的X坐标对减获取元素宽度                    #把左边坐标值加上元素宽度的一半                    #就获取了精确的X值                     Xpoint = (int(coord[2]) - int(coord[0])) / 2.0 + int(coord[0])                      #获取元素的Y坐标                    #通过把元素的上下边界的Y坐标对减获取元素高度                    #把上坐标值加上元素高度度的一半                    #就获取了精确的Y值                     Ypoint = (int(coord[3]) - int(coord[1])) / 2.0 + int(coord[1])                      #返回元素的坐标                    return (elem,Xpoint, Ypoint,clickable)        except Exception:            print "Get the position of element Error!"                        #获取属性值形同的元素列表    def __elements(self, attrib, name):          """         同属性多个元素,返回坐标元组列表         """          #用来放元素坐标        elementList = []         #调用函数,把uidump.xml文件存放在本地临时目录下        self.__uidump()         try:                 tree = ET.ElementTree(file=self.tempFile + "\\uidump.xml")                  treeIter = tree.iter(tag="node")                  for elem in treeIter:                      if elem.attrib[attrib] == name:                          bounds = elem.attrib["bounds"]                        #获取页面元素是否可以点击属性                        clickable=elem.attrib["clickable"]                         #通过数字匹配规则,把bouns合并成一个list                        coord = self.pattern.findall(bounds)                          Xpoint = (int(coord[2]) - int(coord[0])) / 2.0 + int(coord[0])                          Ypoint = (int(coord[3]) - int(coord[1])) / 2.0 + int(coord[1])                         #把元素坐标以元组添加到元素位置列表                         elementList.append((elem,Xpoint, Ypoint,clickable))                  return elementList          except Exception:            print "Get the list of  the  position of elements Error!"                     def findElementByName(self, name):          """         通过元素名称定位         usage: findElementByName(u"设置")         """          return self.__element("text", name)        def findElementsByName(self, name):          return self.__elements("text", name)        def findElementByClass(self, className):          """         通过元素类名定位         usage: findElementByClass("android.widget.TextView")         """          return self.__element("class", className)        def findElementsByClass(self, className):          return self.__elements("class", className)        def findElementById(self, Id):          """         通过元素的resource-id定位         usage: findElementsById("com.android.deskclock:id/imageview")         """          return self.__element("resource-id",Id)        def findElementsById(self, Id):          return self.__elements("resource-id",Id)        #点击元素    def ClickElement(self,dx,dy):        try:            if self.devices:                tap=os.popen("adb shell input tap " + str(dx) + " " + str(dy))             else:                print u"没有连接设备..."        except Exception:            pass        finally:            tap.close()            #滑动设备屏幕                   def swipeScreen(self,x1,y1,x2,y2):        try:            if self.devices:                #滑动设备                swipescreen=os.popen("adb shell input swipe " + str(x1) + " " + str(y1) + " " + str(x2) + " " + str(y2))            else:                print u"没有连接设备..."        except Exception:            pass        finally:            swipescreen.close()              #换新屏幕    def wakeUp(self):        try:            if self.devices:                #唤醒设备                wakeupDevice=os.popen("adb shell input  KEYCODE_MENU")            else:                print u"没有连接设备..."        except Exception:            pass        finally:            wakeupDevice.close()                                        def test():      driver = DeviceDriver()      ele=driver.findElementByName(u"喜马拉雅FM")    driver.ClickElement(ele[1], ele[2])    driver.deviceScreencap()    time.sleep(1)    if __name__=="__main__":    test()  

核心是通过adb shell uiautomator dump命令获取活动页面的UI信息,然后通过xml包,把文件转换成tree!

通过node节点中的属性获取元素元素坐标!

该脚本封装了

findElementByName,findElementsByName,findElementByClass,findElementsByClass,findElementById,findElementsById,ClickElement,swipeScreen,wakeUp等函数用来定位app的元素位置!

通过adb shell input 命令来操作元素的点击!


总结:

app自动化测试中的UI测试是测试的难点与头痛的地方,因为通过appium进行元素定位,不精确而且元素会经常变换,维护成本大!同时,appium环境部署相当复杂!该脚本能完成,元素定位于点击,屏幕滑动等功能!经过测试,操作响应比appium快的多!

后期我会根据把该脚本与monkeyrunner+unittest+robot framework整合在一起!如果写的不到位的希望补充~~~~~~~~~


下面是初步结合如下: