Appium+Python移动自动化测试入门:环境搭建与实战避坑指南
1. 从零到一为什么选择AppiumPython开启你的移动自动化之旅如果你是一名测试工程师、开发人员或者是对自动化技术充满好奇的学习者当你面对市面上数十款移动应用需要验证或者日复一日地重复着点击、输入、滑动等操作时心里一定冒出一个念头能不能让机器来干这些活儿答案是肯定的而AppiumPython这套组合就是你打开移动自动化测试大门最顺手的那把钥匙。我从业十多年从早期的MonkeyRunner、Robotium到后来的Appium亲眼见证了移动测试自动化工具的演进。今天我就以一篇超详细的实战指南带你彻底搞懂如何用Appium和Python启动你的第一个APP避开我当年踩过的所有坑。Appium的核心魅力在于它的“一次编写随处运行”。它基于WebDriver协议这意味着你用来做Web自动化的那套Selenium思路几乎可以平移到移动端。更妙的是它支持Android和iOS双平台你用Python写的一套脚本理论上稍作调整就能测试两个系统的应用。这对于需要覆盖多端业务的团队来说能省下大量的重复劳动成本。Python语言则以其简洁的语法和丰富的生态库大幅降低了自动化脚本的编写和维护门槛。你不需要像用Java那样处理复杂的类结构和编译过程几行清晰的代码就能完成一个测试步骤这让测试逻辑的焦点始终保持在业务本身而不是语言细节上。那么谁适合看这篇内容呢首先是测试新人如果你刚接触自动化想找一个有成就感、能快速看到效果的起点从启动一个APP开始再合适不过了。其次是有一定经验的Web自动化测试工程师想要将技能树扩展到移动端Appium会让你感到非常亲切。最后甚至是开发人员如果你想为自己的APP快速构建一套冒烟测试脚本用Python和Appium也能高效实现。接下来我会假设你从一台干净的电脑开始手把手带你完成环境搭建、核心概念理解、脚本编写到成功运行的全过程。我们不止讲“怎么做”更会深入讲清楚“为什么这么做”以及那些官方文档里不会写的“实战避坑指南”。2. 环境搭建构筑稳定可靠的自动化地基自动化测试就像盖房子环境搭建就是打地基。地基不稳后面所有精巧的脚本都可能瞬间崩塌。很多新手折戟沉沙八成是倒在了环境配置这一步。因此这一部分我会极其详细地拆解每一个环节确保你的环境一次配成避免在环境问题上反复折腾。2.1 核心三剑客Node.js、Appium Server与客户端库Appium的架构是C/S客户端/服务器模式。你的Python脚本是客户端Client它通过WebDriver协议向Appium服务器Server发送指令Appium服务器再将这些指令翻译成手机系统Android的UIAutomator2/iOS的XCUITest能听懂的命令最终驱动手机执行操作。所以我们的环境准备也围绕这三部分展开。第一安装Node.js和npm。Appium Server本身是一个Node.js应用所以我们需要先安装Node.js运行环境。访问Node.js官网下载LTS长期支持版安装包。安装过程很简单一路“下一步”即可。安装完成后打开命令行Windows的CMD或PowerShellMac/Linux的Terminal输入node -v和npm -v来验证安装是否成功并查看版本号。npm是随Node.js一同安装的包管理工具我们用它来安装Appium。注意尽量避免使用操作系统自带的包管理器如Mac的brew安装Node.js有时会导致路径权限问题。直接从官网下载安装包是最稳妥的方式。第二安装Appium Server。有了npm安装Appium Server就一行命令npm install -g appium。这里的-g参数代表全局安装这样你才能在任意目录启动Appium服务。安装过程可能会因为网络原因较慢耐心等待即可。安装完成后通过appium -v检查版本。至此Appium Server就准备好了。但这里有一个至关重要的选择是使用刚才安装的Appium命令行版本还是使用更直观的Appium Desktop图形界面对于纯新手我强烈推荐先使用Appium Desktop。它是一个图形化应用内置了Appium Server和一个非常实用的元素定位工具Inspector。你可以从Appium官网的Release页面下载对应系统的安装包。它的好处是启动服务一键完成Inspector工具可以实时查看手机界面元素并获取它们的定位信息如resource-id、xpath这对编写脚本至关重要。等你熟悉了整个流程和原理后再切换到命令行版本以适应持续集成等自动化部署场景。第三安装Python客户端库。我们的脚本是用Python写的需要安装一个叫Appium-Python-Client的库来和Appium Server通信。使用pip安装即可pip install Appium-Python-Client。这个库封装了所有与Appium Server交互的细节让你能用面向对象的方式轻松编写测试代码。2.2 移动端环境配置以Android为例由于iOS测试需要Mac电脑和苹果开发者账号门槛较高我们以更普及的Android平台作为示例。你需要准备以下几样东西安装Java JDKAndroid开发工具链依赖于Java。去Oracle官网或Adoptium网站下载JDK 8或JDK 11的安装包并安装。安装后需要配置JAVA_HOME环境变量指向你的JDK安装目录例如C:\Program Files\Java\jdk-11.0.xx并将%JAVA_HOME%\bin添加到系统的PATH变量中。在命令行输入java -version验证。安装Android SDK现在谷歌推荐直接安装Android Studio它会自带SDK Manager。安装Android Studio后打开它在欢迎界面找到“More Actions”或配置菜单里的“SDK Manager”。在这里你需要确保安装Android SDK Platform-Tools包含adbAndroid调试桥工具这是电脑与手机通信的桥梁必不可少。至少一个版本的Android SDK Platform例如Android 11.0R的API 30。选择你测试手机对应的或稍低的版本。Android SDK Build-Tools选择一个版本安装。 安装完成后同样需要配置环境变量ANDROID_HOME指向你的SDK根目录例如C:\Users\你的用户名\AppData\Local\Android\Sdk并将%ANDROID_HOME%\platform-tools和%ANDROID_HOME%\tools或tools\bin添加到PATH中。在命令行输入adb version验证。准备一台Android手机或模拟器真机用USB线连接电脑。在手机上进入“开发者选项”通常通过连续点击“设置”-“关于手机”-“版本号”激活开启“USB调试”选项。连接后在电脑命令行输入adb devices如果看到设备序列号并显示device说明连接成功。模拟器可以使用Android Studio自带的AVD Manager创建虚拟设备。我推荐选择Pixel系列机型系统镜像选择不带“Google Play”标志的版本体积小启动快。创建后启动即可adb devices同样能识别到它。实操心得环境变量配置是新手最容易出错的地方。一个检查口诀是“三板斧”java -versionadb versionappium -v。三个命令都能正确输出版本号基础环境才算过关。如果报“不是内部或外部命令”基本就是环境变量PATH没配好。3. 核心概念解析Desired Capabilities与驱动会话环境搭好了我们来聊聊Appium脚本的灵魂——Desired Capabilities。你可以把它理解为你给Appium Server下的一份“订单”或“任务说明书”里面详细描述了你要测试的设备是什么、要启动哪个APP、以及一些行为设置。3.1 Desired Capabilities详解它是一个字典Python中的dict对象在启动测试会话前传递给Appium Server。下面我拆解几个最核心的键值对capabilityplatformName指定操作系统平台。填Android或iOS。这是必填项告诉Appium你要操作的是哪种系统。platformVersion手机系统的版本号。例如11.0。虽然不是绝对必须但明确指定有助于Appium选择更准确的驱动方式。deviceName设备名称。对于Android这个名字可以是任意字符串但通常用来区分不同设备。运行adb devices命令时第一列就是设备名序列号你可以用它也可以用一个易读的名字如MyPixel5。对于iOS真机这个名字必须准确。appPackage和appActivity这是启动Android APP的关键。appPackage是APP的包名相当于APP的身份证。appActivity是你要启动的那个具体界面的名称相当于房间号。如何获取它们有几种方法如果你有APP的源码直接在AndroidManifest.xml里找。使用adb命令先打开你要测试的APP然后在命令行输入adb shell dumpsys window | findstr mCurrentFocusWindows或adb shell dumpsys window | grep mCurrentFocusMac/Linux。输出结果中/前面的就是appPackage后面的就是appActivity。使用APK信息查看工具如aapt或一些图形化工具。automationName自动化引擎。对于Android我们通常使用UiAutomator2Appium 1.6.3推荐它是谷歌官方提供的测试框架功能强大且稳定。对于旧版可能用Appium或Selendroid。noReset是否在会话开始前重置APP状态。如果设为True则不会清除APP数据如登录状态直接从上次退出的地方开始。如果设为False默认每次启动都会像首次安装一样打开APP。根据你的测试场景选择。unicodeKeyboard和resetKeyboard处理中文输入的神器。设为True可以启用Unicode输入法解决自动化中输入中文变成乱码或问号的问题。一个典型的Android Capabilities字典看起来是这样的desired_caps { platformName: Android, platformVersion: 11.0, deviceName: emulator-5554, # 你的设备名来自adb devices appPackage: com.example.myapp, appActivity: .MainActivity, automationName: UiAutomator2, noReset: True, unicodeKeyboard: True, resetKeyboard: True }3.2 驱动对象Driver与会话Session当你把这份“订单”Desired Capabilities通过Appium-Python-Client库提交给Appium Server后Server会尝试根据你的要求连接设备并在设备上初始化测试环境。如果一切顺利它会返回一个会话Session。在代码层面这个会话就体现为你创建的驱动对象WebDriver对象。这个driver对象是你后续所有操作的指挥官。你通过调用它的方法如find_element、click、send_keys来实现对APP的各种操作。创建驱动对象的代码非常简单from appium import webdriver driver webdriver.Remote(http://localhost:4723/wd/hub, desired_caps)这行代码的意思是告诉Python的客户端库去连接本地localhost的4723端口Appium Server默认端口上运行的Appium服务并把我们准备好的desired_caps字典传过去。如果连接成功driver对象就创建好了同时你的手机上也应该启动了指定的APP。4. 实战编写并运行你的第一个启动脚本理论说得再多不如动手一试。现在我们用一个最简化的例子完成从编写脚本到在手机上看到效果的完整闭环。假设我们要启动手机上的“设置”应用几乎所有Android手机都有。4.1 脚本编写步骤拆解首先创建一个新的Python文件比如叫first_start.py。第一步导入必要的库。from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy # 推荐使用新的定位方式 import time这里除了导入webdriver还导入了AppiumBy这是新版客户端库推荐的定位策略枚举比直接用字符串更规范。time库用来添加简单的等待方便我们观察。第二步配置Desired Capabilities。我们需要知道手机“设置”应用的appPackage和appActivity。通过前面提到的adb命令可以查到。以一台Android 11模拟器为例常见的值是appPackage:com.android.settingsappActivity:.Settings(注意前面有点)desired_caps { platformName: Android, platformVersion: 11.0, # 根据你的模拟器/真机版本修改 deviceName: emulator-5554, # 务必替换成你的设备名 appPackage: com.android.settings, appActivity: .Settings, automationName: UiAutomator2, noReset: True, # 不清除设置数据 newCommandTimeout: 600, # 命令超时时间设为600秒 language: zh, # 可选设置语言为中文 locale: CN # 可选设置地区为中国 }注意deviceName必须修改为你通过adb devices查看到的设备标识符否则Appium找不到设备。第三步连接Appium Server并创建驱动。确保你的Appium Desktop已经启动点击“Start Server”按钮或者命令行运行了appium命令。服务默认运行在http://127.0.0.1:4723。# 尝试连接Appium Server try: driver webdriver.Remote(http://127.0.0.1:4723, desired_caps) print(连接Appium Server成功驱动对象已创建) except Exception as e: print(f连接失败错误信息{e}) exit(1) # 连接失败退出脚本第四步添加简单操作与退出。成功启动后我们可以让脚本稍微等一下让我们看清界面然后退出。# 等待5秒观察启动的界面 time.sleep(5) # 获取当前页面的Activity名称验证是否成功进入设置 current_activity driver.current_activity print(f当前Activity是{current_activity}) # 在退出前可以尝试一个简单操作比如获取页面标题如果存在 try: title driver.find_element(AppiumBy.ID, android:id/title) print(f页面标题元素文本可能是{title.text}) except: print(未找到标题元素可能页面结构不同。) # 关闭会话 driver.quit() print(测试完成会话已关闭。)完整的first_start.py脚本如下from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy import time desired_caps { platformName: Android, platformVersion: 11.0, deviceName: emulator-5554, # 请务必修改 appPackage: com.android.settings, appActivity: .Settings, automationName: UiAutomator2, noReset: True, newCommandTimeout: 600, } try: driver webdriver.Remote(http://127.0.0.1:4723, desired_caps) print(连接Appium Server成功驱动对象已创建) except Exception as e: print(f连接失败错误信息{e}) exit(1) time.sleep(5) current_activity driver.current_activity print(f当前Activity是{current_activity}) try: # 不同系统版本的设置页面标题的id可能不同这里是一个常见id title driver.find_element(AppiumBy.ID, android:id/title) print(f页面标题元素文本可能是{title.text}) except Exception as e: print(f查找标题元素时出错{e}) driver.quit() print(测试完成会话已关闭。)4.2 运行脚本与结果分析启动Appium Server打开Appium Desktop确保Host是0.0.0.0Port是4723然后点击“Start Server”。你会看到日志开始滚动。确保设备在线在命令行输入adb devices确认你的设备模拟器或真机状态是device。运行Python脚本在命令行进入你的脚本所在目录执行python first_start.py。预期成功现象命令行打印“连接Appium Server成功驱动对象已创建”你的手机或模拟器屏幕自动亮起并跳转到“设置”应用的主界面。等待5秒后命令行打印出当前的Activity名称可能包含.Settings和找到的标题文本如“设置”、“网络和互联网”等。最后打印“测试完成会话已关闭。”手机上的“设置”应用会退出或回到后台。这个过程看似简单但你已经完成了一个完整的自动化测试闭环环境配置、指令发送、设备响应、结果验证。这是所有复杂自动化测试案例的基石。5. 深度避坑指南与高频问题排查即使按照步骤操作你也可能会遇到各种问题。下面我整理了一份实战中最高频的“坑”及其解决方案这可能是比官方文档更有价值的部分。5.1 连接类问题问题1运行脚本后Appium Server日志报错No devices found或Could not find a connected Android device。排查思路检查设备连接首先在命令行运行adb devices。如果列表为空说明电脑没识别到设备。真机检查USB线、USB调试是否开启、电脑是否安装了手机驱动可尝试使用第三方手机助手安装驱动。模拟器确保模拟器已完全启动进入系统桌面。检查设备名确保脚本中desired_caps里的deviceName和adb devices列出的完全一致。模拟器通常是emulator-5554这种格式真机是一串序列号。重启adb服务有时adb会卡住尝试adb kill-server然后adb start-server。检查Appium Capabilities确认platformVersion与你设备的系统版本大致匹配。问题2Appium Server日志显示An unknown server-side error occurred while processing the command 伴随Original error: Cannot start the com.xxx.xxx application。排查思路检查appPackage和appActivity这是最常见的原因。用adb shell dumpsys window | findstr mCurrentFocus命令再次确认你正在运行的APP的这两个值是否正确。特别注意有些APP的启动Activity不是主界面或者有启动页Splash Activity你需要找到那个真正的入口Activity。APP未安装确保你要启动的APP已经安装在测试设备上。Activity名错误Activity名是大小写敏感的且前面的点.有时代表包名的缩写不能省略。直接从adb命令输出中复制最保险。5.2 运行类问题问题3脚本执行很快但手机上APP闪退或根本没启动。排查思路查看完整日志Appium Desktop的日志窗口信息量巨大不要只看最后几行红字。向上滚动查找在报错之前有没有关于activity、package的警告信息。使用adb logcat在另一个命令行窗口运行adb logcat | findstr -i error或adb logcat *:E可以在脚本运行时实时查看设备上的错误日志这能提供更直接的崩溃原因。尝试关闭noReset将noReset设为False让APP以全新状态启动排除数据损坏的干扰。问题4在输入框无法输入中文输入后变成问号或乱码。解决方案这就是为什么要在Capabilities中设置unicodeKeyboard: True和resetKeyboard: True。它们会启用一个Appium自带的Unicode输入法来处理输入。确保这两个参数已设置。5.3 脚本优化与稳定性技巧技巧1使用隐式等待代替固定sleep在示例中我们用了time.sleep(5)这是一种“强制等待”效率低下且不稳定。更好的方式是使用隐式等待。driver.implicitly_wait(10) # 在创建driver后设置单位秒这行代码告诉driver在查找任何一个元素时如果立即没找到会最多等待10秒期间每隔一段时间重试。一旦找到就继续执行。这能大大提高脚本的健壮性。技巧2善用Appium Desktop的Inspector不要盲目猜测元素该怎么定位。启动Appium Desktop在配置好Capabilities后点击“Start Session”按钮。它会启动APP并打开一个Inspector窗口里面是当前界面的UI层级树和元素属性。你可以直接点击元素查看其resource-id、text、content-desc、class等属性并尝试用这些属性来编写定位代码。这是编写定位语句最直观高效的方式。技巧3会话结束务必driver.quit()养成好习惯在脚本最后或异常处理中一定要调用driver.quit()。它会通知Appium Server结束本次会话释放设备连接。如果只是关闭Python脚本而不quitAppium Server会认为会话还在可能导致设备被占用下次运行时报错。6. 从启动到交互定位元素与简单操作成功启动APP只是第一步自动化测试的核心是与界面元素交互。这就涉及到两个关键点定位元素和执行操作。理解了这部分你就能让脚本“动起来”完成点击、输入、滑动等任务。6.1 八大元素定位策略详解Appium继承了Selenium的定位方式并增加了一些移动端特有的属性。通过AppiumBy类我们可以使用以下主要策略ID定位 (AppiumBy.ID)使用元素的resource-id属性。这是首选且最稳定的定位方式类似于Web中的id。在Inspector中看到resource-id为com.example:id/btn_login就可以用driver.find_element(AppiumBy.ID, com.example:id/btn_login)来定位。Accessibility ID定位 (AppiumBy.ACCESSIBILITY_ID)使用元素的content-desc属性。这个属性原本是为无障碍服务设计的对于没有resource-id但content-desc唯一的元素这是很好的选择。Class Name定位 (AppiumBy.CLASS_NAME)使用元素的类名如android.widget.Button。但一个界面上同类元素太多通常需要结合其他条件。XPath定位 (AppiumBy.XPATH)功能最强大的定位方式可以通过层级关系、属性组合进行精确定位。但性能较差且容易因UI微调而失效应谨慎使用仅在其他定位方式无效时作为备选。例如//android.widget.TextView[text登录]。Android UIAutomator定位 (AppiumBy.ANDROID_UIAUTOMATOR)这是Android平台特有的强大定位器可以使用UIAutomator2的API进行查找语法灵活。例如通过文本定位driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, new UiSelector().text(确定))。通过描述定位new UiSelector().description(关闭按钮)。iOS Predicate String / Class ChainiOS平台特有的定位方式功能强大类似Android的UIAutomator。Name定位 (已弃用)早期版本使用现在基本被Accessibility ID替代。CSS Selector定位仅适用于WebView/H5页面不适用于原生控件。定位策略优先级建议IDAccessibility IDUIAutomatorXPath。优先使用有唯一标识的属性。6.2 基础操作API实战定位到元素后就可以对它进行操作了。以下是最常用的几个方法点击element.click()输入文本element.send_keys(要输入的文本)。输入前通常可以先element.clear()清空原有内容。获取文本text element.text获取属性value element.get_attribute(attributeName)例如获取resource-idid element.get_attribute(resourceId)判断是否显示/可用element.is_displayed()element.is_enabled()让我们写一个稍微复杂点的例子在“设置”里搜索一个选项from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy from appium.webdriver.common.touch_action import TouchAction import time desired_caps { ... } # 同上一个例子 driver webdriver.Remote(http://127.0.0.1:4723, desired_caps) driver.implicitly_wait(10) # 设置隐式等待 # 1. 定位并点击搜索按钮假设它的resource-id是com.android.settings:id/search_action_bar try: search_btn driver.find_element(AppiumBy.ID, com.android.settings:id/search_action_bar) search_btn.click() print(已点击搜索按钮) except Exception as e: print(f未找到搜索按钮{e}) # 可能不同版本设置UI不同尝试通过“搜索”文本定位 search_btn driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, new UiSelector().textContains(搜索)) search_btn.click() # 2. 定位搜索输入框并输入文本 search_box driver.find_element(AppiumBy.ID, android:id/search_src_text) search_box.send_keys(WLAN) # 输入“WLAN”搜索无线网络设置 time.sleep(2) # 等待搜索结果出现 # 3. 从搜索结果中点击匹配的项例如第一个结果 # 假设搜索结果列表的id是android:id/list每一项的布局可能类似 results driver.find_elements(AppiumBy.CLASS_NAME, android.widget.RelativeLayout) if results: # 点击第一个结果 results[0].click() print(已点击第一个搜索结果) else: print(未找到搜索结果) # 4. 滑动操作示例滑动屏幕查看更多内容 # 获取屏幕尺寸 size driver.get_window_size() start_x size[width] * 0.5 start_y size[height] * 0.8 end_x size[width] * 0.5 end_y size[height] * 0.2 # 执行从下往上的滑动 driver.swipe(start_x, start_y, end_x, end_y, 500) # 500毫秒内完成滑动 time.sleep(3) driver.quit()这个例子涵盖了点击、输入、查找多个元素、滑动等操作。注意实际APP的ID和结构需要你用Inspector工具去实际查看不能生搬硬套。7. 框架思维初探让脚本更健壮、易维护当你掌握了启动和基本操作后很快就会面临新的问题脚本写得很乱重复代码多一旦APP界面变化修改起来非常痛苦。这时就需要引入一些简单的框架思维即使不适用完整的测试框架如pytest也能极大提升脚本质量。7.1 封装基础操作与页面对象1. 封装常用操作为函数将常用的操作步骤封装成函数例如一个安全的点击函数def safe_click(driver, by, value, timeout10): 安全点击带显式等待 from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC try: element WebDriverWait(driver, timeout).until( EC.element_to_be_clickable((by, value)) ) element.click() return True except Exception as e: print(f点击元素失败: by{by}, value{value}, error{e}) return False # 使用示例 safe_click(driver, AppiumBy.ID, com.example:id/button)这个函数使用了显式等待它比隐式等待更精确会等待元素满足特定条件如可点击后再操作并提供了超时处理和日志。2. 页面对象模式Page Object雏形这是UI自动化测试中最经典的设计模式。核心思想是将每个APP页面抽象成一个类页面上的元素定位符和基本操作作为这个类的方法。这样测试脚本业务逻辑就和具体的元素定位分离了。class SettingsPage: def __init__(self, driver): self.driver driver # 定义页面元素定位符 self.search_box_id android:id/search_src_text self.wifi_menu_text WLAN def enter_search(self, keyword): 进入搜索并输入关键词 search_btn self.driver.find_element(AppiumBy.ID, com.android.settings:id/search_action_bar) search_btn.click() box self.driver.find_element(AppiumBy.ID, self.search_box_id) box.send_keys(keyword) return self # 支持链式调用 def click_wifi_setting(self): 点击WLAN设置项 # 这里简化处理实际可能需要更精确的定位 element self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, fnew UiSelector().text({self.wifi_menu_text})) element.click() return WifiSettingPage(self.driver) # 返回下一个页面对象 class WifiSettingPage: def __init__(self, driver): self.driver driver def toggle_wifi(self): switch self.driver.find_element(AppiumBy.ID, com.android.settings:id/switch_widget) switch.click() # 在测试脚本中使用 driver webdriver.Remote(..., desired_caps) settings_page SettingsPage(driver) wifi_page settings_page.enter_search(WLAN).click_wifi_setting() wifi_page.toggle_wifi()虽然这个例子很简单但它展示了清晰的层次测试脚本只关心“要做什么”搜索WLAN并开关而“怎么做”元素在哪、如何定位被封装在页面类里。当“WLAN”这个文本改成“无线局域网”时你只需要修改SettingsPage类中的一个字符串所有用到它的测试脚本都无需改动。7.2 配置管理与日志记录1. 分离配置数据不要把desired_caps硬编码在脚本里。可以创建一个config.py文件# config.py def get_desired_capabilities(device_name): base_caps { platformName: Android, automationName: UiAutomator2, noReset: True, newCommandTimeout: 600, unicodeKeyboard: True, resetKeyboard: True, } # 可以根据设备名返回不同的配置 if emulator in device_name: base_caps.update({ platformVersion: 11.0, deviceName: device_name, avd: Pixel_5_API_30 # 指定模拟器名称 }) else: # 真机 base_caps.update({ platformVersion: 12, deviceName: device_name, }) return base_caps APP_SETTINGS { package: com.android.settings, activity: .Settings }在主脚本中导入使用from config import get_desired_capabilities, APP_SETTINGS。2. 添加简单日志使用Python内置的logging模块记录脚本运行的关键步骤和错误便于排查。import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) try: driver webdriver.Remote(..., desired_caps) logger.info(Appium驱动创建成功) except Exception as e: logger.error(f创建驱动失败: {e})把这些好的实践习惯融入你的代码中即使只是一个小脚本也会显得专业、清晰并且为未来扩展成更复杂的自动化项目打下坚实的基础。记住自动化测试代码也是产品代码需要用心设计和维护。

相关新闻

Python CSV转Excel全攻略:pandas、openpyxl、xlwt实战对比与选型指南

Python CSV转Excel全攻略:pandas、openpyxl、xlwt实战对比与选型指南

1. 从CSV到Excel:一个看似简单却暗藏玄机的需求如果你经常和数据打交道,尤其是从各种系统、传感器或者爬虫脚本里导出的数据,CSV文件绝对是你的“老熟人”。它结构简单,纯文本存储,几乎任何编程语言和工具都能轻松处理…

2026/10/18 6:46:36 阅读更多 →
PADS 9.5效率提升:基于EDAHelper实现右键拖拽平移视图

PADS 9.5效率提升:基于EDAHelper实现右键拖拽平移视图

1. 项目缘起:一个被忽视的“效率杀手”如果你用过PADS 9.5,或者更早的版本,你一定对那个操作感到无比熟悉又无比烦躁:想要平移视图,得把鼠标挪到屏幕边缘的滚动条上,或者按住鼠标中键(滚轮&…

2026/10/20 12:29:32 阅读更多 →
两周冲刺阿里云大数据分析师ACP认证:备考策略与实战指南

两周冲刺阿里云大数据分析师ACP认证:备考策略与实战指南

1. 项目概述:为什么选择阿里云大数据分析师认证?如果你正在数据领域摸爬滚打,或者想从其他技术岗位转型,手里没几张像样的证书,心里总有点不踏实。我当初也是这么想的,尤其是在大数据这个赛道,技…

2026/10/17 19:44:28 阅读更多 →

最新新闻

野营用品租赁管理系统:基于SSM的库存与订单实战解析

野营用品租赁管理系统:基于SSM的库存与订单实战解析

野营用品租赁管理系统这个标题,我第一次看到时还觉得挺常规的,真动手做起来才发现它比想象中要麻烦不少。市面上做销售型电商系统的教程一抓一大把,但租赁类业务的核心逻辑完全不同:一个商品不是卖出去就结束了,而是要…

2026/10/20 12:29:17 阅读更多 →
深度学习革命:从神经网络逆袭到大模型时代的技术突围

深度学习革命:从神经网络逆袭到大模型时代的技术突围

人工智能最近这十几年的热闹,几乎都汇聚在一个词上:深度学习。神经网络这条技术路线,曾经被主流学术圈冷落了很多年,如今却成为视觉、语音、语言、决策等几乎所有AI子领域的基础底座。这个系列写到第三篇,我想认真梳理…

2026/10/20 12:29:17 阅读更多 →
rsync增量同步实战:核心机制、常用命令与备份架构

rsync增量同步实战:核心机制、常用命令与备份架构

rsync 这东西,属于那种"会用之后再也回不去"的命令。说白了,它就是 Linux 下做文件同步和增量备份的标配工具,能本地拷贝,也能走 SSH 把数据推到远程服务器,还能在传输时压缩、限速、断点续传,最…

2026/10/20 12:29:17 阅读更多 →
ChatGPT入门提示词指南:100个模板与实操技巧

ChatGPT入门提示词指南:100个模板与实操技巧

1. 为什么“入门提示词”值得单独拎出来讲1.1 一个被大多数人忽略的事实:工具再好,不会提问也白搭我接触过不少刚开始用 ChatGPT 的朋友,发现一个特别有意思的现象:大家注册完账号,打开对话框,第一反应往往…

2026/10/20 12:29:17 阅读更多 →
数据结构期末考试复习攻略:核心考点与算法解题套路

数据结构期末考试复习攻略:核心考点与算法解题套路

期末备考这段时间,最让人头大的课程里,数据结构绝对排得上号。“数据结构期末考试试题及答案”这个搜索词的热度每年都在涨,说明大家不是缺资料,而是缺一份能真正把知识点串起来、知道考什么、怎么答的复习思路。这篇文章我打算用…

2026/10/20 12:29:17 阅读更多 →
JavaScript Map与Set实战指南:性能优化与内存管理要点

JavaScript Map与Set实战指南:性能优化与内存管理要点

在实际开发里,Map和Set这俩内置对象常常被当成“高级特性”一笔带过。大多数教程讲到这里,无非是列一遍 API、丢几个控制台截图,然后就没了。可真到项目里高强度用它们的时候,你会发现一堆文档里查不到的细节:遍历顺序…

2026/10/20 12:28:17 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →