基于python+appium+yaml安卓UI自动化测试分享

发表于:2018-2-07 11:20

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:迈阿密小白    来源:51Testing软件测试网采编

  启动appium服务 StartAppiumServer.py
  主要是启动appium并返回端口port,这个port在下面的driver中需要
  #coding=utf-8
  #author='Shichao-Dong'
  from logs import log
  import random,time
  import platform
  import os
  from GetDevices import devices
  log = log()
  dev = devices().get_deviceName()
  class Sp:
      def __init__(self, device):
          self.device = device
      def __start_driver(self, aport, bpport):
          """
          :return:
          """
          if platform.system() == 'Windows':
              import subprocess
              subprocess.Popen("appium -p %s -bp %s -U %s" %
                               (aport, bpport, self.device), shell=True)
      def start_appium(self):
          """
          启动appium
          p:appium port
          bp:bootstrap port
          :return: 返回appium端口参数
          """
          aport = random.randint(4700, 4900)
          bpport = random.randint(4700, 4900)
          self.__start_driver(aport, bpport)
          log.info(
              'start appium :p %s bp %s device:%s' %
              (aport, bpport, self.device))
          time.sleep(10)
          return aport
      def main(self):
          """
          :return: 启动appium
          """
          return self.start_appium()
      def stop_appium(self):
          '''
          停止appium
          :return:
          '''
          if platform.system() == 'Windows':
              os.popen("taskkill /f /im node.exe")
  if __name__ == '__main__':
      s = Sp(dev)
      s.main()
  获取driver GetDriver.py
  platformName、deviceName、appPackage、appActivity这些卸载配置文件config.ini文件中,可以直接通过readconfig.py文件读取获得。
  appium_port有StartAppiumServer.py文件返回
  s = Sp(deviceName)
  appium_port = s.main()
  def mydriver():
      desired_caps = {
                  'platformName':platformName,'deviceName':deviceName, 'platformVersion':platformVersion,
                  'appPackage':appPackage,'appActivity':appActivity,
                  'unicodeKeyboard':True,'resetKeyboard':True,'noReset':True
                  }
      try:
          driver = webdriver.Remote('http://127.0.0.1:%s/wd/hub'%appium_port,desired_caps)
          time.sleep(4)
          log.info('获取driver成功')
          return driver
      except WebDriverException:
          print 'No driver'
  if __name__ == "__main__":
      mydriver()
  重新封装find等命令,BaseOperate.py
  里面主要是一些上滑、返回、find等一些基础操作
  #coding=utf-8
  #author='Shichao-Dong'
  from selenium.webdriver.support.ui import WebDriverWait
  from logs import log
  import os
  import time
  '''
  一些基础操作:滑动、截图、点击页面元素等
  '''
  class BaseOperate:
      def __init__(self,driver):
          self.driver = driver
      def back(self):
          '''
          返回键
          :return:
          '''
          os.popen("adb shell input keyevent 4")
      def get_window_size(self):
          '''
          获取屏幕大小
          :return: windowsize
          '''
          global windowSize
          windowSize = self.driver.get_window_size()
          return windowSize
      def swipe_up(self):
          '''
          向上滑动
          :return:
          '''
          windowsSize = self.get_window_size()
          width = windowsSize.get("width")
          height = windowsSize.get("height")
          self.driver.swipe(width/2, height*3/4, width/2, height/4, 1000)
      def screenshot(self):
          now=time.strftime("%y%m%d-%H-%M-%S")
          PATH = lambda p: os.path.abspath(
              os.path.join(os.path.dirname(__file__), p)
          )
          screenshoot_path = PATH('../results/screenshoot/')
          self.driver.get_screenshot_as_file(screenshoot_path+now+'.png')
      def find_id(self,id):
          '''
          寻找元素
          :return:
          '''
          exsit = self.driver.find_element_by_id(id)
          if exsit :
              return True
          else:
              return False
      def find_name(self,name):
          '''
          判断页面是否存在某个元素
          :param name: text
          :return:
          '''
          findname = "//*[@text='%s']"%(name)
          exsit = self.driver.find_element_by_xpath(findname)
          if exsit :
              return True
          else:
              return False
      def get_name(self,name):
          '''
          定位页面text元素
          :param name:
          :return:
          '''
          # element = driver.find_element_by_name(name)
          # return element
          findname = "//*[@text='%s']"%(name)
          try:
              element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element_by_xpath(findname))
              # element = self.driver.find_element_by_xpath(findname)
              self.driver.implicitly_wait(2)
              return element
          except:
              self.screenshot()
              log.error('未定位到元素:'+'%s')%(name)
      def get_id(self,id):
          '''
          定位页面resouce-id元素
          :param id:
          :return:
          '''
          try:
              element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element_by_id(id))
              # element = self.driver.find_element_by_id(id)
              self.driver.implicitly_wait(2)
              return element
          except:
              self.screenshot()
              log.error('未定位到元素:'+'%s')%(id)
      def get_xpath(self,xpath):
          '''
          定位页面xpath元素
          :param id:
          :return:
          '''
          try:
              element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element_by_xpath(xpath))
              # element = self.driver.find_element_by_xpath(xpath)
              self.driver.implicitly_wait(2)
              return element
          except:
              self.screenshot()
              log.error('未定位到元素:'+'%s')%(xpath)
      def get_ids(self,id):
          '''
          定位页面resouce-id元素组
          :param id:
          :return:列表
          '''
          try:
              # elements = self.driver.find_elements_by_id(id)
              elements = WebDriverWait(self.driver, 10).until(lambda x: x.find_elements_by_id(id))
              self.driver.implicitly_wait(2)
              return elements
          except:
              self.screenshot()
              log.error('未定位到元素:'+'%s')%(id)
      def page(self,name):
          '''
          返回至指定页面
          :return:
          '''
          i=0
          while i<10:
              i=i+1
              try:
                  findname = "//*[@text='%s']"%(name)
                  self.driver.find_element_by_xpath(findname)
                  self.driver.implicitly_wait(2)
                  break
              except :
                  os.popen("adb shell input keyevent 4")
                  try:
                      findname = "//*[@text='确定']"
                      self.driver.find_element_by_xpath(findname).click()
                      self.driver.implicitly_wait(2)
                  except:
                      os.popen("adb shell input keyevent 4")
                  try:
                      self.driver.find_element_by_xpath("//*[@text='工作台']")
                      self.driver.implicitly_wait(2)
                      break
                  except:
                      os.popen("adb shell input keyevent 4")
  Operate.py
  我认为最关键的一步了,后面没有page都是调用这个文件进行测试,主要是根据读取的yaml文件,然后进行if...else...判断,根据对应的operate_type分别进行对应的click、sendkeys等操作
  #coding=utf-8
  #author='Shichao-Dong'
  from GetYaml import getyaml
  from BaseOperate import BaseOperate
  class Operate:
      def __init__(self,path,driver):
          self.path = path
          self.driver = driver
          self.yaml = getyaml(self.path)
          self.baseoperate=BaseOperate(driver)
      def check_operate_type(self):
          '''
          读取yaml信息并执行
          element_info:定位元素信息
          find_type:属性,id、xpath、text、ids
          operate_type: click、sendkeys、back、swipe_up 为back就是返回,暂时就三种
          上面三个必填,operate_type必填!!!!!!
          send_content:send_keys 时用到
          index:ids时用到
          times:
          :return:
          '''
          for i in range(self.yaml.caselen()):
              if self.yaml.get_operate_type(i) == 'click':
                  if self.yaml.get_findtype(i) == 'text':
                      self.baseoperate.get_name(self.yaml.get_elementinfo(i)).click()
                  elif self.yaml.get_findtype(i) == 'id':
                      self.baseoperate.get_id(self.yaml.get_elementinfo(i)).click()
                  elif self.yaml.get_findtype(i) == 'xpath':
                      self.baseoperate.get_xpath(self.yaml.get_elementinfo(i)).click()
                  elif self.yaml.get_findtype(i) == 'ids':
                      self.baseoperate.get_ids(self.yaml.get_elementinfo(i))[self.yaml.get_index(i)].click()
              elif self.yaml.get_operate_type(i) == 'send_keys':
                  if self.yaml.get_findtype(i) == 'text':
                      self.baseoperate.get_name(self.yaml.get_elementinfo(i)).send_keys(self.yaml.get_send_content(i))
                  elif self.yaml.get_findtype(i) == 'id':
                      self.baseoperate.get_id(self.yaml.get_elementinfo(i)).send_keys(self.yaml.get_send_content(i))
                  elif self.yaml.get_findtype(i) == 'xpath':
                      self.baseoperate.get_xpath(self.yaml.get_elementinfo(i)).send_keys(self.yaml.get_send_content(i))
                  elif self.yaml.get_findtype(i) == 'ids':
                      self.baseoperate.get_ids(self.yaml.get_elementinfo(i))[self.yaml.get_index(i)].send_keys(self.yaml.get_send_content(i))
              elif self.yaml.get_operate_type(i) == 'back':
                  for n in range(self.yaml.get_backtimes(i)):
                      self.baseoperate.back()
              elif self.yaml.get_operate_type(i) == 'swipe_up':
                  for n in range(self.yaml.get_backtimes(i)):
                      self.baseoperate.swipe_up()
      def back_home(self):
          '''
          返回至工作台
          :return:
          '''
          self.baseoperate.page('工作台')
  公共部分的代码就介绍这么多,在编写这个框架的时候,大部分精力都花在这部分,所以个人觉得还是值得好好研究的
  Page部分
  page部分是最小用例集,一个模块一个文件夹,以客户为例,
  目前写了两个用例,一个新增,一个排序,文件如下:
  file.png
  代码如下,非常的简洁,
  import sys
  reload(sys)
  sys.setdefaultencoding('utf8')
  import codecs,os
  from public.Operate import Operate
  from public.GetYaml import getyaml
  PATH = lambda p: os.path.abspath(
      os.path.join(os.path.dirname(__file__), p)
  )
  yamlpath = PATH("../../testyaml/cm/cm-001addcm.yaml")
  class AddcmPage:
      def __init__(self,driver):
          self.path = yamlpath
          self.driver = driver
          self.operate = Operate(self.path,self.driver)
      def operateap(self):
          self.operate.check_operate_type()
      def home(self):
          self.operate.back_home()
  运行用例
  这部分用了unittest,运行所有测试用例和生成报告。
  一个模块一个用例,以客户为例:CmTest.py
  from page.cm.CmAddcmPage import AddcmPage
  from page.cm.CmSortcmPage import SortcmPage
  from public.GetDriver import mydriver
  driver = mydriver()
  import unittest,time
  class Cm(unittest.TestCase):
      def test_001addcm(self):
          '''
          新增客户
          :return:
          '''
          add = AddcmPage(driver)
          add.operateap()
          add.home()
      def test_002sortcm(self):
          '''
          客户排序
          :return:
          '''
          sort = SortcmPage(driver)
          sort.sortlist()
          sort.home()
      def test_999close(self):
          driver.quit()
          time.sleep(10)
  if __name__ == "__main__":
      unittest.main()
  首先从page层将需要运行的用例都import进来,然后用unittest运行即可。
  如果想要运行所有的测试用例,需要用到runtest.py
  import time,os
  import unittest
  import HTMLTestRunner
  from testcase.CmTest import Cm
  def testsuit():
      suite = unittest.TestSuite()
      suite.addTests([unittest.defaultTestLoader.loadTestsFromTestCase(Cm),
  ])
      # runner = unittest.TextTestRunner(verbosity=2)
      # runner.run(suite)
      now=time.strftime("%y-%m-%d-%H-%M-%S")
      PATH = lambda p: os.path.abspath(
          os.path.join(os.path.dirname(__file__), p)
      )
      dirpath = PATH("./results/waiqin365-")
      filename=dirpath + now +'result.html'
      fp=open(filename,'wb')
      runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title='waiqin365 6.0.6beta test result',description=u'result:')
      runner.run(suite)
      fp.close()
  if __name__ =="__main__":
      testsuit()
  这边的思路差不多,也是先导入再装入suite即可
  总结
  就目前而言,暂时算是实现了数据与用例的分离,但是yaml的编写要求较高,不能格式上出错。
  同时也有一些其他可以优化的地方,如:
  对弹窗的判断
  断开后重连机制
  失败后重跑机制
  等等,后续可以根据需求进行优化
  最后再贴一下开源地址Github,有兴趣的小伙伴可以去看一下,欢迎拍砖
  备注:完成过程中参考了Louis-me 和 auto 这两个开源项目,感谢!!!

22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号