Appium Appium 开源分享优化版

lose · 发布于 2018年01月19日 · 最后由 wch 回复于 2018年02月03日 · 1773 次阅读
本帖已被设为精华帖!

之前分享过PageObject+Python+Appium

本版本是对上次版本较大改版,主要解决了:

  • 失败重连一次(默认一次)可配置多次
  • 基于appium1.7.1 uiautomator2
  • 解决uiautomator2启动超时
  • 新增检查点关键字,contrary_getval,exceptsl,contrary暂时没有写到yml配置文件中,直接在写在了page层
    • 检查点关键字contrary:相反检查点,传1表示如果检查元素存在就说明失败,如删除后,此元素依然存在
    • 检查点关键字toast: 表示提示框检查点
    • 检查点关键字contrary_getval: 相反值检查点,如果对比成功,说明失败
    • 检查点关键字check: 自定义检查结果
    • 检查点关键字excepts: 如果为1,表示操作出现异常情况检查点为成功。如:删除成功了,此数据不存在则为真
  • 测试报告新增对多台测试机结果统计(安卓)
  • 适配mac,windows平台
  • 适配ios测试(简单调试通过,后续有机会实际去使用和优化)

代码简要分析

yml测试用例

testinfo:
    - id: test004
      title: 每日新闻卡片浏览记录
      info: 打开知识
testcase:

    - element_info: com.huawei.works.knowledge:id/title
      find_type: ids
      index: 0
      operate_type: get_value
      info: 获取每日新闻下对第一条数据
    - element_info: com.huawei.works.knowledge:id/title
      find_type: ids
      index: 0
      operate_type: click
      is_time: 3
      info: 点击每日新闻下对第一条数据
    - element_info: h5-scroll
      find_type: id
      is_webview: 1 # 切换到webview
      info: 查找并获取详情页标题
    - element_info: com.huawei.works.knowledge:id/vtb_img_left
      find_type: id
      is_webview: 2 # 切换到native
      operate_type: click
      info: 点击返回按钮
    - element_info: com.huawei.works.knowledge:id/vtb_img_right2
      find_type: id
      operate_type: click
      info: 点击首页历史记录按钮
check:
    - element_info: com.huawei.works.knowledge:id/browser_knowledge_history_text
      find_type: ids
      index: 0
      operate_type: get_value
      info: 查找是否存在历史记录

某个用例的page层

from PageObject import Pages


class DayNewHistoryPage:
    '''
    每日新闻浏览历史
    '''

    def __init__(self, kwargs):
        _init = {"driver": kwargs["driver"], "path": kwargs["path"], "device": kwargs["device"],
                 "logTest": kwargs["logTest"], "caseName": kwargs["caseName"]}
        self.page = Pages.PagesObjects(_init)

    def operate(self):  # 操作步骤
        self.page.operate()

    def checkPoint(self):  # 检查点
        self.page.checkPoint()
  • pages再次封装了一层,主要可以看下重连机制的实现
    • 其实主要用的是launch_app+setupclass,另外一个好处是避免用例依赖,并不会重新启动一个session
if result is not True and be.RE_CONNECT:
          self.msg = "用例失败重连过一次,失败原因:" + self.testInfo[0]["msg"]
          self.logTest.buildStartLine(self.caseName + "_失败重连")  # 记录日志
          self.operateElement.switchToNative()
          self.driver.launch_app()
          self.isOperate = True
          self.get_value = []
          self.is_get = False
          self.operate() # 执行步骤
          result = self.check(kwargs) # 坚持点
          self.testInfo[0]["msg"] = self.msg
      self.operateElement.switchToNative()

testcase层调用page层

class HomeTest(ParametrizedTestCase):
    # 首页下拉
    def testAHomeSwipeDown(self):
        app = {}
        app["logTest"] = self.logTest
        app["driver"] = self.driver
        app["path"] = PATH("../yaml/home/HomeSwipeDown.yaml")
        app["device"] = self.devicesName
        app["caseName"] = sys._getframe().f_code.co_name

        page = HomeSwipeDownPage(app)
        page.operate()
        page.checkPoint()

    # banner浏览历史记录
    def testB0annerHistory(self):
        app = {}
        app["logTest"] = self.logTest
        app["driver"] = self.driver
        app["path"] = PATH("../yaml/home/BannerHistory.yaml")
        app["device"] = self.devicesName
        app["caseName"] = sys._getframe().f_code.co_name
        page = BannerHistoryPage(app)
        page.operate()
        page.checkPoint()

    @classmethod
    def setUpClass(cls):
        super(HomeTest, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        super(HomeTest, cls).tearDownClass()

代码入口

def runnerCaseApp(devices):
    starttime = datetime.now()
    suite = unittest.TestSuite()
    suite.addTest(ParametrizedTestCase.parametrize(HomeTest, param=devices))
    suite.addTest(ParametrizedTestCase.parametrize(TestWeiQunTest, param=devices))
    unittest.TextTestRunner(verbosity=2).run(suite)

比较麻烦case处理

  • 当遇到有些用例比较麻烦,必须单独写page 层,比如长按交换空间位置
  • 自定义page层
.....
    def operate(self):
        for item in self.testCase:
            m_s_g = self.msg + "\n" if self.msg != "" else ""

            result = self.operateElement.operate(item, self.testInfo, self.logTest, self.device)
            if not result["result"]:
                msg = "执行过程中失败,请检查元素是否存在" + item["element_info"]
                print(msg)
                self.msg = m_s_g + msg
                self.testInfo[0]["msg"] = msg
                self.isOperate = False
                return False

            if item.get("operate_type", "0") == "location":
                app = {}
                web_element = self.driver.find_elements_by_id(item["element_info"])[item["index"]]
                start = web_element.location
                # 获取控件开始位置的坐标轴
                app["startX"] = start["x"]
                app["startY"] = start["y"]
                # 获取控件坐标轴差
                size1 = web_element.size

                width = size1["width"]
                height = size1["height"]
                # 计算出控件结束坐标
                endX = width + app["startX"]
                endY = height + app["startY"]

                app["endX"] = endX - 20
                app["endY"] = endY - 60

                self.location.append(app)
                # self.driver.swipe(endX, endY, starty, endY)
            if item.get("operate_type", "0") == be.GET_VALUE:
                self.get_value.append(result["text"])

            if item.get("is_swpie", "0") != "0":
                print(self.location)
                self.driver.swipe(self.location[0]["endX"], self.location[0]["endY"], self.location[1]["endX"], self.location[1]["endY"]+10)
  • yaml用例 可以自定义一些关键字给page用
testinfo:
    - id: test019
      title: 拖动排序知识卡片
      info: 打开知识
testcase:
    - element_info: com.huawei.works.knowledge:id/vtb_img_right
      find_type: id
      operate_type: click
      info: 点击排序卡片按钮
    - element_info: com.huawei.works.knowledge:id/my_card_item_name_text
      find_type: ids
      index: 1
      operate_type: get_value
      info: 得到第二个卡片的值
    - element_info: com.huawei.works.knowledge:id/drag_item_image
      find_type: ids
      index: 0
      operate_type: location
      info: 得到第一卡片的坐标
    - element_info: com.huawei.works.knowledge:id/drag_item_image
      find_type: ids
      index: 1
      operate_type: location
      is_swpie: 1 # 特殊关键字,滑动指令
      info: 得到第二个卡片的坐标并拖动
    - element_info: com.huawei.works.knowledge:id/vtb_img_left
      find_type: id
      operate_type: click
      info: 点击返回按钮
check:
    - element_info: com.huawei.works.knowledge:id/title_txt
      find_type: id
      operate_type: get_value

其他

  • 顺便说下遇到浮动层无法点击
    • 浮动层造成可以识别到元素,触发了点击却失效的处理方法是,得到元素坐标,然后用adb shell方式去触发,感兴趣的可以看下adb_tap关键字的封装
  • 其他更多优化可以看我的CHANGELOG
  • 开源地址
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 8 条回复
104 seveniruby 将本帖设为了精华贴 01月20日 02:48
4481

mark一下

64049f

楼主好,我有个疑惑,之前试用过你的老版本框架,发现每个用例执行完毕后都会回到系统桌面,然后再次吊起APP主界面,继续执行下一个用例,后来看代码似乎是由于每条用例执行都会调用下面的setup和tearDown。使用没问题,只是感觉正常的应该是进入APP,所有用例完成后退出。不知道新改版后的代码,是否还有这个现象,对python不是很了解,还请解惑哈。
@classmethod
def setUpClass(cls):
super(HomeTest, cls).setUpClass()

@classmethod
def tearDownClass(cls):
super(HomeTest, cls).tearDownClass()

6504
lose · 4楼 · 2018年01月24日 作者
64049fLaen 回复

l新版本帖子中也说了用的是setupclass和launch_app的方式,避免了每个用例的依赖,同时也没有像setup那样每次都安装和设置appium setting

4f9cc2 harsayer python appium UI 自动化测试框架讨论 中提及了此贴 02月01日 10:48
4f9cc2

创建文件/home/cmd/workspace/git/appium/Log/info.pickle成功
创建文件/home/cmd/workspace/git/appium/Log/sum.pickle成功
创建文件/home/cmd/workspace/git/appium/Log/devices.pickle成功

创建这几个文件 和 *.pickle 是做什么用的 能介绍下吗?

B7dcb8
仅楼主可见
6504
lose · 8楼 · 2018年02月02日 作者
4f9cc2harsayer 回复

测试报告使用,sum是统计所有用例,info是详情页的报告,devices是对应所有设备用例的执行统计

6504
lose · 9楼 · 2018年02月02日 作者
B7dcb8wch 回复

代码我写死了启动类,你可以改下,下次更新我改成自动获取apk

B7dcb8
仅楼主可见
Ca50b1 wuhenyan 线下班第二期 Bash 教程 中提及了此贴 02月03日 21:06
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册