1. 环境搭建:别怕,一步步来

很多朋友一听到“环境搭建”四个字就头疼,总觉得是拦路虎。其实,Appium的环境搭建就像拼装一个乐高模型,只要零件齐全、按图索骥,一步步来,真没想象中那么难。我刚开始接触的时候也踩过不少坑,比如环境变量配错、版本不匹配导致的各种奇葩报错,但现在回头看,只要理清脉络,整个过程半小时就能搞定。这篇文章,我就把自己这些年趟过的路、踩过的坑,总结成一份保姆级的实战指南,目标是让你跟着操作一遍,就能在自己的电脑上把Appium跑起来,并且能写出第一个自动化测试脚本。

咱们的目标很明确:搭建一个能同时支持Android和iOS(以Android为主)自动化测试的Appium环境。别被吓到,我保证用最直白的话把每个步骤讲清楚。整个环境可以看作由几个核心“零件”组成:编程语言与客户端库Appium服务器移动端SDK以及设备连接。下面我们就来逐一“采购”和“组装”。

1.1 安装Python与必要的库

Python是咱们写自动化脚本的语言,Appium-Python-Client则是连接脚本和Appium服务器的桥梁。首先,确保你电脑上安装了Python,建议用Python 3.7以上的版本,太老的版本可能会有兼容性问题。打开你的命令行(Windows是CMD或PowerShell,Mac是终端),我们来安装最关键的库。

pip install Appium-Python-Client

这条命令会从Python官方的软件仓库下载安装。如果觉得下载慢,可以换成国内的镜像源,速度会快很多,比如用清华的源:

pip install Appium-Python-Client -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完Appium客户端,别忘了它的好搭档Selenium。因为Appium底层扩展了WebDriver协议,所以需要Selenium。为了保证版本兼容,最好也更新一下:

pip install selenium -U

这里有个小经验:有时候项目依赖的Selenium版本比较固定,如果你全局更新后导致其他项目出问题,可以考虑使用虚拟环境(virtualenv或conda)来为这个Appium项目创建一个独立的环境,这样库版本之间就不会打架了。

1.2 安装Appium服务器

Appium服务器是一个独立的服务程序,它负责接收我们Python脚本发过来的指令,然后翻译成手机能听懂的命令去操作手机。有两种主要安装方式,我强烈推荐新手使用第一种:Appium Desktop

这是一个带图形界面的桌面应用,不仅包含了服务器,还内置了一个超好用的元素定位器(Inspector),对于调试和编写脚本帮助巨大。你去它的GitHub发布页面,找到最新版本,根据你的操作系统(Windows、macOS)下载对应的安装包,像安装普通软件一样安装即可。安装好后打开,你会看到一个简洁的界面,可以一键启动服务器。

另一种方式是通过Node.js用命令行安装,这对于喜欢纯命令行操作或者需要在无界面的服务器上运行的同学更合适。先安装Node.js,然后用npm命令安装:

npm install -g appium

安装完成后,在命令行输入 appium 就能启动服务了。我个人的习惯是,本地开发和调试用Appium Desktop,方便直观;持续集成(CI)环境用命令行版本,更稳定易集成。

1.3 配置Android开发环境(SDK与JDK)

这是搭建Android测试环境的核心部分,需要安装Java开发工具包(JDK)和Android SDK。别担心,我们不需要成为Android开发专家,只需要把它们当作工具来安装配置。

首先是JDK。Appium服务器本身是用Node.js写的,但它的底层驱动(特别是对于Android)需要调用Java工具。去Oracle官网或OpenJDK网站下载JDK 8或JDK 11的安装包进行安装。安装时记住安装路径,比如 C:\Program Files\Java\jdk1.8.0_301。安装完后,需要配置著名的JAVA_HOME环境变量。在系统环境变量里,新建一个变量,变量名JAVA_HOME,变量值就是你的JDK安装路径。然后,在Path变量里,添加%JAVA_HOME%\bin。打开新的命令行窗口,输入 java -versionjavac -version,如果都能正确显示版本号,说明配置成功了。

接下来是Android SDK。现在谷歌官方推荐直接下载Android Studio这个集成开发环境,它自带了SDK。但对于我们自动化测试来说,其实只需要SDK的命令行工具就够了,这样更轻量。你可以从安卓开发者网站找到“Command line tools only”进行下载。下载后解压到一个你喜欢的目录,比如 D:\Android\sdk

同样需要配置环境变量:新建 ANDROID_HOME,值设为SDK的根目录路径。然后在 Path 里添加两个关键路径:%ANDROID_HOME%\tools%ANDROID_HOME%\platform-tools。配置完成后,新开一个命令行,输入 adb version,如果看到ADB(Android Debug Bridge)的版本信息,就大功告成了。ADB是我们和手机沟通的“瑞士军刀”,后面会经常用到。

1.4 连接真机或模拟器

环境配好了,总得有个“手机”来跑测试吧。最真实的选择当然是真机。用USB线连接你的Android手机,在手机设置里狂点“关于手机”里的“版本号”7次,开启“开发者选项”。然后在开发者选项里,打开“USB调试”。这时在电脑命令行输入 adb devices,你应该能看到你的设备号,后面跟着 device 字样,这就表示连接成功了。如果显示 unauthorized,去手机上看看是不是弹出了调试授权对话框,点允许就行。

另一个选择是模拟器。如果你没有多余的手机,或者需要测试不同型号、系统版本的设备,模拟器是绝佳选择。Android SDK里自带的模拟器(通过AVD Manager创建)可以用,但比较慢。我更喜欢用Genymotion或者官方的Android Studio模拟器,它们速度更快,功能也更完善。用模拟器时,同样需要通过 adb connect <模拟器IP地址> 的方式来连接,之后的测试流程就和真机一模一样了。

2. 核心配置与启动:写出你的第一行代码

环境就绪,设备连上,现在终于到了激动人心的时刻:写代码启动Appium并连接设备!这个过程的核心是一个叫做 Desired Capabilities 的字典(或者叫JSON对象),你可以把它理解成一份给Appium服务器的“需求说明书”,告诉它:我想用什么系统、哪个版本的手机、测试哪个APP、怎么启动等等。

2.1 理解Desired Capabilities

这个配置字典是Appium脚本的“心脏”,每一个键值对都有其特定含义。我刚开始学的时候,就是对着文档一个个参数试,才慢慢弄明白的。下面我结合一个最常用的Android配置示例,给你掰开揉碎了讲:

from appium import webdriver

desired_caps = {
    # 1. 平台信息:告诉Appium是安卓还是iOS
    'platformName': 'Android',
    # 设备安卓版本,写大版本号就行,比如'10',不用写'10.1.1'
    'platformVersion': '10',
    # 设备名,这个名字可以自定义,主要用于在日志里标识设备,不和具体硬件绑定
    'deviceName': 'My_Pixel_5',

    # 2. 应用信息:你要测试哪个APP?
    # APP的包名,可以理解为应用的身份证号
    'appPackage': 'com.example.myapp',
    # APP的启动Activity名,可以理解为应用的主入口大门
    'appActivity': 'com.example.myapp.MainActivity',

    # 3. 会话与设备行为控制
    # 是否在会话开始前重置应用状态(清除数据)
    'noReset': True,
    # 是否在会话结束后完全卸载APP
    'fullReset': False,
    # 设置命令超时时间,单位秒,防止脚本卡死
    'newCommandTimeout': 300,

    # 4. 自动化引擎相关
    # 指定使用的自动化驱动,UIAutomator2是当前安卓的推荐选项,更稳定强大
    'automationName': 'UIAutomator2',
    # 自动授予APP所需的权限,避免测试时弹出权限申请框
    'autoGrantPermissions': True,

    # 5. 输入法相关(可选但常用)
    # 启用Unicode输入,方便输入中文等特殊字符
    'unicodeKeyboard': True,
    # 测试结束后重置回系统默认输入法
    'resetKeyboard': True,
}

这里有几个参数我特别想强调一下。appPackageappActivity 是启动指定APP的关键。怎么获取它们呢?有个很实用的ADB命令:在手机打开目标APP,然后在电脑命令行输入 adb shell dumpsys window windows | findstr mFocusedApp(Windows)或 adb shell dumpsys window windows | grep mFocusedApp(Mac/Linux),输出结果里就有包名和Activity信息。automationName 强烈建议设为 UIAutomator2,它是谷歌官方维护的框架,对现代安卓应用支持更好。

2.2 启动会话并连接设备

配置字典写好,启动就非常简单了。Appium服务器默认运行在本地的4723端口,我们用一行代码就能创建驱动(driver)对象,这个对象就是我们控制手机的“遥控器”。

# 假设你的Appium服务器就在本机运行
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

执行这行代码前,请确保你已经用Appium Desktop或命令行启动了Appium服务器(看到 [Appium] Welcome to Appium... 之类的日志)。代码执行后,你会看到手机上的目标APP被自动启动,同时命令行服务器日志里会滚动各种信息,这表示连接成功了!

之后,我们可以设置一个全局的隐式等待,这就像给脚本一点耐心,让它去查找元素时如果没立刻找到,会等待一段时间而不是立刻报错。

driver.implicitly_wait(10) # 单位是秒

踩坑提醒:这里最容易出的问题就是端口冲突或者Capabilities配置错误。如果连接失败,第一件事就是去看Appium服务器的日志,里面的错误信息通常非常详细,能直接指出是哪个参数有问题或者设备没准备好。

3. 元素定位:找到屏幕上的每一个按钮

自动化测试的本质就是模拟人的操作:点这里、输入那里、滑动屏幕。而“点这里”的前提是,你得告诉脚本“这里”是哪里。这就是元素定位,它是自动化脚本的基石,也是新手花费时间最多的地方。Appium提供了丰富的定位策略,我把它分为两大类:基于属性定位基于坐标定位

3.1 使用Appium Inspector辅助定位

在真正写定位代码前,我强烈推荐你使用Appium Desktop自带的Inspector工具。它就像给你的脚本装上了“眼睛”。启动Appium Server后,在界面里启动Inspector会话,填入刚才的Desired Capabilities,点击连接。它会启动APP并加载出当前页面的UI结构树和截图。

在结构树里点击任何一个元素,右边就会显示这个元素的所有属性:resource-idtextclasscontent-descbounds坐标等等。你可以直接在这里尝试各种定位表达式,看能不能唯一定位到目标元素。这个工具能极大提升你编写和调试定位代码的效率,避免盲目猜测。

3.2 八大定位方法实战详解

有了Inspector帮我们看清元素属性,现在来看看在代码里怎么用。Appium的定位方法和Selenium非常相似,如果你有Web自动化经验,会感到非常亲切。

  1. 通过ID定位 (resource-id):这是最理想、最稳定的定位方式,就像人的身份证号。

    # 定位登录按钮,假设它的resource-id是‘com.xx.app:id/btn_login’
    login_button = driver.find_element_by_id(‘com.xx.app:id/btn_login’)
    login_button.click()
    

    注意:find_element_by_id 这个方法内部就是使用 resource-id 来查找的。

  2. 通过Accessibility ID定位 (content-desc):这个属性原本是为无障碍服务设计的,如果开发同学填写了,用它定位非常精准。

    # 定位搜索图标,它的content-desc是‘搜索’
    search_icon = driver.find_element_by_accessibility_id(‘搜索’)
    
  3. 通过XPath定位:这是最强大但也最复杂的定位方式。当元素没有唯一ID时,XPath可以通过层级关系、属性组合来定位。

    # 定位一个文本为‘提交’的按钮
    submit_btn = driver.find_element_by_xpath(“//android.widget.Button[@text=‘提交’]”)
    # 通过父元素的id和子元素的class来定位
    item = driver.find_element_by_xpath(“//*[@resource-id=‘list’]//android.widget.TextView”)
    

    使用XPath要小心,过于复杂的路径会降低定位速度且易受UI改动影响。尽量用相对路径,避免用绝对路径(以/开头)。

  4. 通过Class Name定位:通过元素的控件类型来定位,比如所有 TextViewButton。但通常同类元素太多,不唯一,所以多用于查找元素列表。

    # 找到当前页面所有的文本视图
    all_textviews = driver.find_elements_by_class_name(“android.widget.TextView”)
    for textview in all_textviews:
        print(textview.text)
    
  5. 通过Android UIAutomator定位:这是Appium为安卓定制的大杀器,语法功能非常强大,支持文本匹配、描述匹配、滚动查找等。

    # 使用UiSelector通过文本定位
    element = driver.find_element_by_android_uiautomator(‘new UiSelector().text(“确定”)’)
    # 通过文本包含关系定位
    element = driver.find_element_by_android_uiautomator(‘new UiSelector().textContains(“确认”)’)
    # 通过父子关系定位
    element = driver.find_element_by_android_uiautomator(‘new UiSelector().resourceId(“parent_id”).childSelector(new UiSelector().className(“android.widget.Button”))’)
    
  6. 通过iOS Predicate/String定位:如果你测试的是iOS应用,那么 find_element_by_ios_predicatefind_element_by_ios_class_chain 是更高效的选择,它们使用苹果原生的查询语法。

  7. 通过坐标定位:这是最后的手段,当元素实在无法通过属性定位时(比如游戏里的某个图案),才使用绝对坐标。但要注意,不同分辨率设备坐标不同,兼容性差。

    # 点击屏幕坐标 (100, 200) 的位置
    driver.tap([(100, 200)])
    
  8. 通过相对坐标/百分比定位:比绝对坐标稍好一点,通过计算屏幕尺寸的百分比来点击。

    size = driver.get_window_size()
    width = size[‘width’]
    height = size[‘height’]
    # 点击屏幕横向中点,纵向1/4的位置
    driver.tap([(width * 0.5, height * 0.25)])
    

定位策略黄金法则:优先级上,ID/Accessibility ID > XPath (简洁版) > UIAutomator > 其他。尽量让开发同学给关键元素加上唯一的 resource-id,这是对自动化测试最友好的支持。

4. 常用操作API:让手机“动”起来

定位到元素后,我们就可以对它进行各种操作了。Appium的API设计得很人性化,很多方法名一看就懂。这里我把最常用、最核心的操作分成几类,并分享一些实战中的技巧和坑。

4.1 点击、输入与清除

这是最基础的三板斧。

  • 点击 (click()):非常简单,定位到元素后直接调用。

    login_button.click()
    

    坑点:有时候元素明明找到了,一点击就报错“元素不可点击”。这通常是因为元素还没真正处于可交互状态(比如动画还没播放完)。这时候除了用 implicitly_wait,还可以用 显式等待

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.support import expected_conditions as EC
    from appium.webdriver.common.touch_action import TouchAction
    
    # 等待元素可被点击,最多等10秒
    button = WebDriverWait(driver, 10).until(
        EC.element_to_be_clickable((MobileBy.ID, ‘com.xx.app:id/btn’))
    )
    button.click()
    
  • 输入 (send_keys()):向输入框输入文本。

    username_input = driver.find_element_by_id(‘username’)
    username_input.send_keys(‘testuser’)
    

    技巧:输入前最好先 clear() 一下,确保输入框是空的,避免残留字符影响。

    username_input.clear()
    username_input.send_keys(‘new_user’)
    
  • 清除 (clear()):清空输入框内容。

4.2 滑动与滚动操作

滑动是移动端APP最频繁的操作之一,查看列表、刷新页面都离不开它。Appium提供了 swipe 和更灵活的 TouchAction

  • swipe 简单滑动:给定起点和终点的坐标,以及滑动持续时间(毫秒)。

    # 从屏幕中央向下滑动到上方,模拟下拉刷新
    start_x = width / 2
    start_y = height * 0.6
    end_x = width / 2
    end_y = height * 0.2
    driver.swipe(start_x, start_y, end_x, end_y, 500) # 500ms内完成
    
  • TouchAction 复杂手势TouchAction 可以组合出更复杂的手势,比如长按、拖拽、多点触控。

    from appium.webdriver.common.touch_action import TouchAction
    actions = TouchAction(driver)
    # 长按某个元素2秒
    element = driver.find_element_by_id(‘some_id’)
    actions.long_press(element).wait(2000).release().perform()
    # 从元素A拖拽到元素B
    element_a = driver.find_element_by_id(‘A’)
    element_b = driver.find_element_by_id(‘B’)
    actions.press(element_a).wait(1000).move_to(element_b).release().perform()
    
  • 滚动到指定元素:这是一个更高级但更实用的操作,直接滚动直到某个元素出现在屏幕上。

    # 这是一个通过Android UIAutomator实现的滚动查找,非常高效
    target_element = driver.find_element_by_android_uiautomator(
        ‘new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text(“目标文本”))’
    )
    

4.3 系统按键与通知栏操作

自动化测试经常需要模拟按下Home键、返回键、音量键等。

  • 常用按键

    driver.press_keycode(3)   # Home键
    driver.press_keycode(4)   # 返回键 (Back)
    driver.press_keycode(82)  # 菜单键 (Menu)
    driver.press_keycode(24)  # 音量+
    driver.press_keycode(25)  # 音量-
    driver.press_keycode(26)  # 电源键
    
  • 操作通知栏

    # 打开通知栏
    driver.open_notifications()
    # 操作完通知后,通常按一下返回键或Home键关闭
    driver.press_keycode(4)
    

4.4 获取元素属性与页面源码

在写断言或者做逻辑判断时,我们经常需要获取元素的状态或页面的信息。

  • 获取元素属性

    element = driver.find_element_by_id(‘some_id’)
    print(element.text)          # 获取文本
    print(element.get_attribute(‘checked’)) # 获取是否选中等属性
    print(element.location)      # 获取坐标
    print(element.size)          # 获取大小
    
  • 获取页面源码:这在动态分析页面结构时非常有用。

    page_source = driver.page_source
    # page_source是XML格式的字符串,包含了当前页面的所有UI层级信息
    # 你可以用它来检查某个元素是否存在,或者用XML解析库来分析
    if “欢迎回来” in page_source:
        print(“登录成功页面已加载”)
    

5. 处理混合应用与WebView

现代很多APP都不是纯粹的“原生应用”,而是混合了原生控件和网页(WebView)的混合应用。比如APP里嵌入的一个活动页面、一个帮助文档,很可能就是一个内嵌的浏览器页面。测试这类页面,需要特殊的处理方式。

5.1 理解Context的概念

这是处理WebView的核心。你可以把Context理解为不同的“操作环境”。Appium启动后,默认处于 NATIVE_APP 这个Context下,在这里你只能用前面讲的方法定位和操作原生控件。当APP打开一个WebView页面时,就会多出一个或多个类似 WEBVIEW_com.xx.app 的Context。你需要先切换到对应的WebView Context,才能使用Selenium那套定位网页元素的方法(如 find_element_by_css_selector)。

5.2 切换Context的实战步骤

  1. 获取所有Context:首先列出当前可用的所有Context。

    contexts = driver.contexts
    print(contexts) # 输出可能为:[‘NATIVE_APP’, ‘WEBVIEW_com.xx.app’]
    
  2. 切换到WebView Context:然后切换到你想操作的WebView。

    webview_context = contexts[1] # 假设第二个是WebView
    driver.switch_to.context(webview_context)
    

    切换后,你的 driver 就变成了一个“网页浏览器驱动”,可以像做Web自动化一样操作了。

  3. 操作网页元素:使用Selenium的定位方法。

    # 现在可以定位网页里的输入框、按钮了
    search_input = driver.find_element_by_css_selector(‘#kw’)
    search_input.send_keys(‘Appium’)
    search_button = driver.find_element_by_css_selector(‘#su’)
    search_button.click()
    
  4. 切回原生Context:操作完WebView,记得切回来。

    driver.switch_to.context(‘NATIVE_APP’)
    

5.3 关键前提与常见问题

要让这套流程跑通,有几个必须满足的前提条件,这也是最容易卡住新手的地方:

  • APP必须开启WebView调试:这需要开发同学在代码中为WebView组件设置 setWebContentsDebuggingEnabled(true)。对于测试线上的APP,如果没开启,这步就无法进行。
  • 需要对应的ChromeDriver:Appium操作WebView底层用的是ChromeDriver,而且这个ChromeDriver的版本必须和你手机里WebView(或Chrome浏览器)的版本匹配。你可以在 desired_caps 中通过 chromedriverExecutableDirchromedriverExecutable 参数指定你下载好的、版本匹配的ChromeDriver路径。
  • 如何查看WebView版本:在手机浏览器(或APP内)打开 chrome://inspect 是官方方法,但可能需要网络环境。更简单的方法是用ADB命令:adb shell dumpsys webviewupdate | grep Current

处理WebView的自动化确实比原生复杂,但一旦配置成功,就能将Web自动化的强大能力应用到APP测试中,覆盖更全面的场景。

6. 框架设计与最佳实践

当你能熟练地编写单个测试步骤后,就要开始思考如何组织代码,让它更健壮、更易维护、更容易扩展。好的框架设计能让你的自动化测试事半功倍,而不是陷入无尽的调试和修改泥潭。

6.1 使用Page Object模式

这是UI自动化测试中最重要的设计模式,没有之一。它的核心思想是将页面封装成对象,将页面元素定位和操作细节隐藏在对象内部。测试脚本只关心业务逻辑,不关心具体怎么点按钮。

举个例子,一个登录页面 LoginPage

class LoginPage:
    def __init__(self, driver):
        self.driver = driver
        # 将元素定位信息集中管理
        self.username_input = (MobileBy.ID, ‘com.xx.app:id/username’)
        self.password_input = (MobileBy.ID, ‘com.xx.app:id/password’)
        self.login_button = (MobileBy.ID, ‘com.xx.app:id/login_btn’)
        self.error_msg = (MobileBy.ID, ‘com.xx.app:id/error_tv’)

    def enter_username(self, username):
        # 内部处理等待、查找、输入等细节
        WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located(self.username_input)
        ).send_keys(username)

    def enter_password(self, password):
        WebDriverWait(self.driver, 10).until(
            EC.presence_of_element_located(self.password_input)
        ).clear()
        self.driver.find_element(*self.password_input).send_keys(password)

    def click_login(self):
        self.driver.find_element(*self.login_button).click()

    def get_error_message(self):
        try:
            return self.driver.find_element(*self.error_msg).text
        except:
            return None

# 在测试脚本中,使用起来非常清晰
def test_login_failure():
    driver = get_driver() # 获取驱动的函数
    login_page = LoginPage(driver)
    login_page.enter_username(‘wrong_user’)
    login_page.enter_password(‘wrong_pwd’)
    login_page.click_login()
    assert “密码错误” in login_page.get_error_message()

这样做的好处太明显了:元素定位信息只在一处维护,如果UI改了,你只需要修改 LoginPage 这个类里的定位符,所有用到这个页面的测试脚本都不用动。脚本的可读性也大大提升。

6.2 数据驱动测试

把测试数据和测试逻辑分离。不要将用户名、密码等测试数据硬编码在脚本里,而是放在外部文件(如JSON、YAML、Excel、CSV)或数据库中。使用像 pytest 这样的框架,可以很方便地实现参数化。

import pytest
import json

# 从JSON文件加载测试数据
with open(‘test_data/login_data.json’, ‘r’) as f:
    test_cases = json.load(f)

@pytest.mark.parametrize(“username, password, expected”, test_cases)
def test_login_with_data(driver, username, password, expected):
    login_page = LoginPage(driver)
    login_page.enter_username(username)
    login_page.enter_password(password)
    login_page.click_login()
    if expected == ‘success’:
        # 断言登录成功
        assert HomePage(driver).is_displayed()
    else:
        # 断言出现对应的错误提示
        assert expected in login_page.get_error_message()

这样,增加新的测试用例只需要在数据文件里加一行,不需要改代码,极大地提高了测试的覆盖率和维护效率。

6.3 异常处理与日志记录

自动化脚本运行在复杂的环境里,网络波动、页面加载慢、弹窗干扰都可能导致失败。健壮的脚本必须有良好的异常处理和日志记录。

  • 使用Try-Except处理预期异常:比如处理偶尔出现的弹窗。

    def safe_click(element_locator):
        try:
            driver.find_element(*element_locator).click()
        except ElementClickInterceptedException:
            # 如果被弹窗挡住,先关闭弹窗
            close_popup()
            driver.find_element(*element_locator).click()
    
  • 详细记录日志:使用Python的 logging 模块,在关键步骤(如启动APP、点击按钮、验证结果)记录信息、警告和错误。当测试失败时,详细的日志是排查问题的第一手资料。可以将日志输出到文件,并配上时间戳和日志级别。

  • 失败截图:这是定位UI问题最直观的方式。在测试用例的 teardown 阶段,或者在任何异常捕获中,对当前屏幕进行截图保存。

    def take_screenshot(driver, name):
        timestamp = time.strftime(“%Y%m%d_%H%M%S”)
        filename = f”screenshot_failure_{name}_{timestamp}.png”
        driver.save_screenshot(filename)
        logging.info(f”截图已保存至:{filename}”)
    

6.4 集成到CI/CD流水线

个人玩玩,在本地运行脚本就够了。但对于团队项目,必须将自动化测试集成到持续集成/持续部署(CI/CD)流水线中,比如Jenkins、GitLab CI、GitHub Actions。这样每次代码提交都能自动触发测试,快速得到质量反馈。

在CI中运行Appium测试,通常需要一个稳定的测试环境,这可能是一台专用的物理手机,更常见的是使用云测平台(如国内的Testin、腾讯WeTest,或国外的BrowserStack、Sauce Labs)提供的真机集群,或者使用Docker容器化的模拟器。你需要将你的测试脚本、依赖和环境配置打包,并编写CI的配置文件(如Jenkinsfile、.gitlab-ci.yml),指定触发条件、安装依赖、启动Appium服务、运行测试脚本、收集测试报告和日志等步骤。

这条路走通后,自动化测试才真正从“玩具”变成了提升团队研发效率的“利器”。它不再只是测试人员的手头工具,而是成为了交付流程中不可或缺的质量关卡。

更多推荐