一.移动自动化测试的技术体系

  1. UI 自动化分类

    1. Web 自动化:Selenium

    2. App 自动化:Appium

    3. GUI 自动化:Windows、Mac

    4. 微信小程序自动化:App + Web 测试技术的外延形态

  2. 自动化测试框架选择

    1. 推荐Appium

      1. 跨语言:Java、Python、Node.js 等

      2. 跨平台:Android、iOS、Windows、Mac

      3. 底层多引擎可切换

      4. 生态丰富,社区强大(testerHome

    2. iOS: KIFWDA、XCTest

    3. Android: Robotium、UI Automator、UI Automator2

    4. 特色框架:calabash、Macaca、ATX

二.准备工作

  1. Appium 简介
    1.  Appium 是一个开源的、跨平台的测试框架,可以用来测试 Native App、混合应用、移动 Web 应用(H5 应用)等,也是当下互联网企业实现移动自动化测试的重要工具。
    2. AppiumAppium-desktopAppium Client 的区别是 Appium 体系的核心,因为它本身也是一个 web 接口服务,所以也会被成为 appium server,对外默认开启包括 4723 等多个端口。
    3. Appium-Desktop:是为了让 Appium 能够更好用,让小白入门更容易,让调试和界面分析更方便,官方开发了 GUI 的工具 Appium-desktop。
    4. Appium Client:Appium 只是一个 Web 接口,他接受 HTTP 请求,所以各个语言都可以自己封装发送请 求,于是就有 appium 下的各个子项目。以下是各语言对 Appium Client 做的支持。

    5. Appium 的设计理念

    6. Appium 引擎列表 
  2.  Appium 的安装 (Mac 版)
    1. npm 方式安装 (官方安装,国际通用,网络不稳定,不建议使用此方式安装)​​​​​​
      > brew install node      # get node.js
      > npm install -g appium  # get appium
      
    2. cnpm 方式安装 (推荐)
      1.  npm install -g cnpm --registry=https://registry.npm.taobao.org
      2. cnpm install -g appium
      3. ​​​pip3 install Appium-Python-Client
  3.  注意事项

    1.  pip 问题:由于mac系统默认安装了python2.7且pip已经不对python2进行管理,导致pip安装失败。
      解决:安装python3,配置path环境变量,否则zsh会报command not found;将本地的python alias python3路径(可参考
      这里

    2. 安装python IDE,使用pycharm 社区版本即可,专业版需要付费

    3. iOS真机证书问题1:由于appium 是基于WebDriverAgent,找到appium文件下WebDriverAgent位置打开配置即可,appiumdesktop的WebDriverAgent通常是在
      /Applications/Appium.app/Contents/Resources/app/node_modules/appium/node_modules/appium-webdriveragent/WebDriverAgent.xcodeproj

    4.  iOS真机证书问题2: 如果配置玩证书仍旧提示证书问题,可能是由于WebDriverAgent工程的位置没和appium 对应上,根据server提示的路径(一般是在这里/usr/local/lib/node_modules/appium/node_modules/appium-webdriveragent/WebDriverAgent.xcodeproj)找到WebDriverAgent工程配置即可

 三.小试牛刀      

  1. 启动appiumdesktop点击start server
  2. server启动成功
  3. start inspector session
  4. 配置caps参数,官网介绍https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/caps.md
    此处配置了:平台、自动化测试框架、设备名称、udid、teamid、开发环境、app路径、每次执行用例是否重置
  5. 点击start进入 inspector
    inspector提供了图层树,方便定位元素;点击、滑动、输入、刷新、语言选择及用例录制等功能
  6. inspector操作对应的log会在appiumserver控制台输出如下
  7. 常用定位手段

    1. id

    2. Accessibility ID

    3. XPath

  8. 元素定位符与复用

    1. findElementByXXX

    2. findElement(by, value)

  9. 常见自动化动作支持

    1. click

    2. sendKeys

    3. swipe

    4. touch action

  10. 常用的设备交付命令

    1. 模拟电话、短信

    2. 横竖屏切换

    3. App处理

    4. 键盘、粘贴板

    5. 录屏

 四.使用pycharm编写用例

        inspector虽然也可以生成用例,可是代码可读性较低,难以维护,为了使用例的可维护行增强,建议使用IDE来编写测试用例

1.配置caps,封装启动过程

# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python

from datetime import datetime
from time import sleep
import pytest
from appium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

from pageObject.TLMainPage import TLMainPage


class App:
    driver: WebDriver = None

    @classmethod
    def start(cls):
        caps = {}
        caps["platformName"] = "ios"
        caps["automationName"] = "xcuitest"
        caps["deviceName"] = "iPhone 8P"
        caps["udid"] = "5394fc48a76b80e4aff8adacbb1256bc809a6b58"
        caps["xcodeOrgId"] = "E6YTL27QWA"
        caps["xcodeSigningId"] = "iPhone Developer"
        caps["app"] = "/Users/chenwuqi/Documents/appium/VSTimeLine.app"
        caps["noReset"] = False
        caps["autoAcceptAlerts"] = True

        cls.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
        cls.driver.implicitly_wait(1)#隐式等待

        print("1-------------" + datetime.now().strftime(format("%H:%M:%S")))

        def loaded(driver):
            print("2-------------" + datetime.now().strftime(format("%H:%M:%S")))
            if len(cls.driver.find_elements_by_id("btn skip@3x")) >= 1:
                cls.driver.find_element_by_id("btn skip@3x").click()
                return True
            else:
                return False

        try:
            WebDriverWait(cls.driver, 40).until(loaded)#显示等待
        except:
            print("no update")
        return TLMainPage(cls.driver)

    @classmethod
    def quit(cls):
        cls.driver.quit()
  • 创建caps字典,将初始化参数配置好,主要告诉appium当前的测试平台(Android、iOS)、自动化测试框架、设备名称、udid、teamid、开发环境、app路径、每次执行用例是否重置;
  • 这里多了一个autoAcceptAlerts,其主要作用是为了解决系统弹窗问题,由于系统弹窗不属于我们app管理,我们无法识别该弹窗,apple提供了相应API方便自动化测试处理,appium以启动参数的形式供我们配置使用;
  • 隐式等待:implicitly_wait,适用于通用场景,服务端(Appium)会在特定的超时时间内重试多次寻找控件,目的是提高测试用例的健壮性,考虑到系统的动画通常默认时间是0.3s,以及页面绘制等其他消耗,所以这里我们设置了1秒;
  • 显示等待:WebDriverWait(cls.driver, 40).until(loaded),适用于特定场景,比如说我们app启动页的跳过按钮,显示出来可能要10s,这种情况隐式等待是不方便处理的;显示等待通常会有一个最大等待时间和等待条件;我们这里是在40s内循环判断条件是否满足,如果超时则抛出异常

2.设计模式,为了增强测试用例的可维护性,防止出现修改一个页面所有用例都不能用的情况,我们采用page object模式         

3.封装基类   



from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webdriver import WebDriver


class TLBasePage:
    _black_list = [
        (By.ID, "同意"),
        (By.ID, "我知道了"),
        (By.ID, "icon close security")
    ]#黑名单列表,处理弹窗

    def __init__(self, driver:WebDriver):
        self.driver = driver

    '''
    异常处理,主要是处理查找异常
    1.查找不到元素时,去查询黑名单,去除弹窗遮挡
    2.多个弹窗嵌套,导致获取page source 更新不及时问题,添加重试机制
    '''
    def handle_exception(self):
        print(":exception")
        self.driver.implicitly_wait(3)
        for locactor in self._black_list:
            print(locactor)
            elements = self.driver.find_elements(*locactor)
            if len(elements) >= 1:
                #TODO:不是所有的弹框处理都是要点击弹框,可根据业务需要自行封装
                elements[0].click()
            else:
                print("%s not fund" % str(locactor))


        self.driver.implicitly_wait(1)

    def find_element(self, locator):
        print(locator)
        try:
            element = self.driver.find_element(*locator)
            return element
        except:
            retryCount = 2
            while retryCount > 0:
                self.handle_exception()
                elements = self.driver.find_elements(*locator)
                for element in elements:
                    if element.is_displayed():
                        return element
                retryCount = retryCount - 1
            print("%s not fund" % str(locator))


    def find_element_and_click(self, locator):
        try:
            #如果click也有异常,可以这样处理
            element = self.find_element(locator)
            element.click()
        except:
            self.handle_exception()
            element = self.find_element(locator)
            element.click()

  4.封装首页page 类

from selenium.webdriver.support.wait import WebDriverWait

from pageObject.TLBasePage import TLBasePage
from selenium.webdriver.common.by import By
from pageObject.TLVideoProcessPage import TLVideoProcessPage

class TLMainPage(TLBasePage):
    _newer_tutorial = (By.ID, "新手教程")
    _draft_box = (By.ID, "草稿箱")
    _draft_first_item = (By.ID, "加伍磅视频打卡挑战")
    # 加伍磅视频打卡挑战

    def change_tab(self):
        self.find_element_and_click(self._newer_tutorial)
        self.find_element_and_click(self._draft_box)

    def to_videoProcess(self):
        self.change_tab()
        self.find_element_and_click(self._draft_first_item)
        def loaded(driver):
            if len(self.driver.find_elements_by_id("抠像素材")) >= 1:
                return True
            else:
                self.find_element_and_click(self._draft_first_item)
                return False

        try:
            WebDriverWait(self.driver, 40).until(loaded)
        except:
            print("no launch page")
        return TLVideoProcessPage(self.driver)

    5.封装视频处理page

from selenium.webdriver.support.wait import WebDriverWait

from pageObject.TLBasePage import TLBasePage
from selenium.webdriver.common.by import By
from pageObject.TLVideoProcessPage import TLVideoProcessPage

class TLMainPage(TLBasePage):
    _newer_tutorial = (By.ID, "新手教程")
    _draft_box = (By.ID, "草稿箱")
    _draft_first_item = (By.ID, "加伍磅视频打卡挑战")
    # 加伍磅视频打卡挑战

    def change_tab(self):
        self.find_element_and_click(self._newer_tutorial)
        self.find_element_and_click(self._draft_box)

    def to_videoProcess(self):
        self.change_tab()
        self.find_element_and_click(self._draft_first_item)
        def loaded(driver):
            if len(self.driver.find_elements_by_id("抠像素材")) >= 1:
                return True
            else:
                self.find_element_and_click(self._draft_first_item)
                return False

        try:
            WebDriverWait(self.driver, 40).until(loaded)
        except:
            print("no update")
        return TLVideoProcessPage(self.driver)

 6.测试用例


from selenium.webdriver.support.wait import WebDriverWait

from pageObject.app import App


class TestDemo:
    def setup(self):
        self.videoProcess_page = App.start().to_videoProcess()

    def test_courseDetail_back(self):
        self.videoProcess_page.change_bottomTab()

    def teardown(self):
        App.quit()

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐