Appium的Client/Server结构
  appium的核心其实是一个暴露了一系列REST API的server
  这个server的功能其实很简单:监听一个端口,然后接收由client发送来的command。翻译这些command,把这些command转成移动设备可以理解的形式发送给移动设备,然后移动设备执行完这些command后把执行结果返回给appium server,appium server再把执行结果返回给client。
  在这里client其实是发起command的设备,一般来说是我们代码执行的机器,执行appium测试代码的机器。狭义点理解,可以把client理解成是代码,这些代码可以是java/ruby/python/js的,只要它实现了webdriver标准协议可以。
  Appium-Python-Client
  appium client是对webdriver原生api的一些扩展和封装。它可以帮助我们更容易的写出用例,写出更好懂的用例,并且它是配合原生的webdriver来使用的,因此二者必须配合使用缺一不可。
  推荐的安装方法:
  pip install Appium-Python-Client
  HTMLTestRunner
  下载传送门
  HTMLTestRunner是Python标准库中单元测试模块的扩展,它生成易于使用的html测试报告
  下载HTMLTestRunner.py文件后,把HTMLTestRunner文件放到Python27Lib的目录下即可
  具体使用方法见文章末尾处的完整用例代码
  Appium的Session
  session是一个会话,在webdriver/appium,你的所有工作永远都是在session start后才可以进行的。一般来说,通过POST /session这个URL,然后传入Desired Capabilities可以开启session了。
  开启session后,会返回一个全局的session id,以后几乎所有的请求都必须带上这个session id,因为这个seesion id代表了你所打开的浏览器或者是移动设备的模拟器。
  Appium的Desired Capabilities
  Desired Capabilities在启动session的时候是必须提供的。
  Desired Capabilities本质上是key value的对象,它告诉appium server这样一些事情:
  · 本次测试是启动浏览器还是启动移动设备?
  · 是启动andorid还是启动ios?
  · 启动android时,app的package是什么?
  · 启动android时,app的activity是什么?
  Appium的Desired Capabilities是扩展了webdriver的Desired Capabilities的,下面是一些通用配置:
  · automationName:使用哪种自动化引擎。appium(默认)还是Selendroid?
  · platformName:使用哪种移动平台。iOS, Android, orFirefoxOS?
  · deviceName:启动哪种设备,是真机还是模拟器?iPhone Simulator, iPad Simulator, iPhone Retina 4-inch, Android Emulator, Galaxy S4, etc...
  · app:应用的路径,注意一定是路径。如果指定了appPackage和appActivity的话,这个属性是可以不设置的。另外这个属性和browserName属性是冲突的。
  · browserName:移动浏览器的名称。比如Safari' for iOS and 'Chrome', 'Chromium', or 'Browser' for Android;与app属性互斥。
  下面的属性是android平台特定的:
  · appActivity:待测试的app的Activity名字。比如com.yangcong345.android.phone.presentation.activity.SplashActivity
  · appPackage:待测试的app的java package。比如com.yangcong345.android.phone
  控件定位
  在appium的定位方法中,下面这些方法是可以为我们使用的。也是说,我们通过下面几个约定好的方式,按照webdriver和appium的DSL(自行搜索并理解)进行控件特征的描述和定位。
  · find by "class" (ui component type,andorid上可以是android.widget.TextView)
  · find by "xpath" (an abstract representation of a path to an element, with certain constraints,由于appium的xpath库不完备的原因,这个不太推荐)
  · find by "id"(android上是控件的resource id)
  · find by name(如android的Button的名字)
  定位方法代码展示:
  self.driver.find_elements_by_class_name('android.widget.TextView')
  self.driver.find_element_by_xpath("//android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.view.ViewGroup[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.LinearLayout[1]/android.widget.LinearLayout[1]/android.widget.Button[1]")
  self.driver.find_element_by_id('com.yangcong345.android.phone:id/rbStudent')
  self.driver.find_element_by_name(u'人教版')
  常用方法
  官方Demo传送门
  当定位到一个控件或是一个控件组的时候(find element跟find elements的区别),可以对控件执行一些事件动作,比如点击,滑动等
  代码展示:
  self.driver.find_element_by_id('com.yangcong345.android.phone:id/rbStudent').click()
  els = el.find_elements_by_class_name('android.widget.TextView')
  self.driver.scroll(els[len(els) - 1], els[0])
  重点:进度条如何定位到末尾
  el = self.driver.find_element_by_id('com.yangcong345.android.phone:id/mediacontroller_progress')
  self.assertIsNotNone(el)
  end = el.size.get('width')
  y = el.location.get('y')
  action = TouchAction(self.driver)
  action.tap(None, end + 95, y).perform()//此处tap方法有个bug,是参数需要传None才能使action的定位生效
  还有常用的方法有:
  · 输入框输入字符
  · 获取控件的属性
  · 一些断言方法
  代码展示:
self.driver.find_element_by_id('com.yangcong345.android.phone:id/etUserName').send_keys('qq3@qqq.com')
el.get_attribute('checked')//返回的是字符串
self.assertEqual(el.get_attribute('checked'), 'true')
self.assertIsNotNone(el)
//还有很多断言方法在此不列举了