Airtest+Selenium自动化测试实战:从零搭建混合模式脚本

1. 项目概述:为什么选择Airtest+Selenium组合拳?

如果你刚接触自动化测试,面对一堆工具名词可能有点懵:Selenium我知道,是Web自动化的老大哥,那Airtest又是什么?为什么要把它们俩放一起?这恰恰是这个方案的精妙之处。我最初接触自动化时,也走过弯路,要么用纯Selenium写脚本,调试起来像在走钢丝,一个元素定位失败就全盘卡住;要么用一些录制工具,生成的代码像天书,维护成本极高。直到我发现了Airtest-Selenium这个“缝合怪”,才感觉找到了新手和老手都能上手的平衡点。

简单来说,Airtest是一个由网易开源的、基于图像识别的自动化测试框架,它的核心优势是“所见即所得”——你不用太关心网页背后复杂的HTML结构,直接对屏幕上看到的按钮、输入框截图,它就能帮你点进去、输入文字。这对0基础的朋友极其友好,大大降低了门槛。而Selenium则是Web自动化领域的标准,它通过直接操作浏览器驱动,能精准地控制网页元素,执行复杂的逻辑判断,是自动化测试稳定性和深度的保证。把它们结合起来,就像是给汽车装上了自动驾驶(Airtest的图像识别)和手动驾驶(Selenium的代码控制)两套系统,你可以根据路况随时切换,既保证了易用性,又不失灵活性。

这次我们以Firefox浏览器为目标,一是因为它对WebDriver协议支持非常标准,环境配置相对简单;二是在某些特定场景(如测试浏览器插件、兼容性测试)下,Firefox是必不可少的。这个教程的目标,就是让你在完全不懂代码的情况下,也能搭建起环境,并成功运行第一个自动化测试脚本,感受“机器帮你点点点”的魔力。

2. 环境搭建与工具安装:从零开始的避坑指南

万事开头难,自动化测试的第一步——环境搭建,就劝退了不少人。网上教程很多,但版本冲突、路径问题、驱动不对应等坑一个接一个。下面我结合自己踩过的所有坑,给你梳理一条最清晰的路径。我们的核心是安装三个东西:Python、Airtest-Selenium库、Firefox浏览器及其驱动。

2.1 Python环境安装与配置

Python是我们的脚本运行环境。别怕,安装它比装一个游戏还简单。

  1. 下载安装:直接访问Python官网,下载最新的稳定版(比如3.10或3.11)。安装时,务必勾选“Add Python to PATH”这个选项。这是最重要的一步,勾选后系统才能识别python命令。很多新手卡住,就是因为没勾这个。
  2. 验证安装:安装完成后,打开命令行(Windows按Win+R,输入cmd;Mac打开终端)。输入python --version并回车。如果显示类似“Python 3.10.0”的版本号,恭喜你,第一步成功了。如果提示“不是内部或外部命令”,那就需要手动添加环境变量,具体路径在你安装Python的目录下。

注意:不建议安装太新的Python版本(如3.12+),因为一些库的兼容性可能还没跟上。选择3.8-3.11之间的版本最为稳妥。

2.2 安装Airtest-Selenium与Selenium库

有了Python,安装库就一行命令的事。但这里有个关键点:我们不是安装airtest,而是安装airtest-selenium。它封装了Selenium,并加入了Airtest的图像识别能力。

继续在刚才的命令行里,输入以下命令:

pip install airtest-selenium

这条命令会自动安装airtest-selenium以及它所依赖的selenium库。pip是Python的包管理工具,安装时可能会因为网络问题慢或失败,可以尝试在后面加上-i https://pypi.tuna.tsinghua.edu.cn/simple来使用国内镜像加速。

安装完成后,可以输入pip list查看已安装的包,确认能看到airtest-seleniumselenium

2.3 Firefox浏览器与GeckoDriver驱动部署

这是最容易出错的一步。Selenium控制浏览器,需要一个“翻译官”,这就是浏览器驱动(Driver)。

  1. 安装Firefox浏览器:如果你没有,去Mozilla官网下载并安装最新稳定版的Firefox。记住它的安装位置(通常不需要特别记住,但知道没坏处)。
  2. 下载GeckoDriver:这是Firefox的驱动。前往GeckoDriver的GitHub发布页。关键来了:驱动版本必须和你的Firefox浏览器大版本号匹配!比如你安装了Firefox 115,就去找支持Firefox 115的GeckoDriver版本。下载对应你操作系统的文件(Windows是.zip,Mac是.tar.gz)。
  3. 配置GeckoDriver
    • 简单方法(推荐给新手):将下载解压后得到的geckodriver.exe(Windows)或geckodriver(Mac/Linux)文件,直接扔到Python的安装目录下(和python.exe在同一文件夹)。因为Python安装目录通常已在系统PATH中,这样Selenium就能自动找到它。
    • 标准方法:将geckodriver所在目录的路径,添加到系统的环境变量PATH中。具体操作可以搜索“如何添加环境变量”,不同系统略有不同。

验证驱动是否可用:打开命令行,输入geckodriver --version。如果能看到版本信息输出,说明驱动配置成功。如果提示命令找不到,请检查上述路径配置。

3. 第一个脚本:从图像识别开始感受自动化

环境齐备,我们来写第一个真正意义上的自动化脚本。这个脚本的目标是:打开百度首页,在搜索框输入“自动化测试”,然后点击搜索按钮。我们将先用Airtest最核心的图像识别功能来实现,让你直观感受它的便利。

创建一个新的文本文件,将其后缀改为.py,例如first_test.py。用任何文本编辑器(推荐VSCode、Sublime Text甚至记事本)打开它,输入以下代码:

from airtest_selenium.proxy import WebFirefox from airtest.core.api import * # 1. 创建一个WebFirefox对象,这会启动Firefox浏览器 driver = WebFirefox() # 2. 设置浏览器窗口最大化,并访问百度 driver.maximize_window() driver.get("https://www.baidu.com") # 3. 使用Airtest的图像识别功能定位搜索框并输入文字 # touch()函数用于点击或定位图片。这里我们传入一张搜索框的截图。 # 你需要先对百度首页的搜索框进行截图,保存为“search_box.png”,并放在与脚本同目录下。 touch(Template(r"search_box.png")) # 在定位到的位置输入文字 text("自动化测试") # 4. 同样,对“百度一下”按钮截图,保存为“search_button.png”,并点击 touch(Template(r"search_button.png")) # 5. 等待几秒,看看搜索结果页面 sleep(5) # 6. 关闭浏览器 driver.quit()

代码解读与实操要点

  • WebFirefox():这是airtest-selenium提供的类,它初始化了一个同时支持Selenium操作和Airtest图像识别的Firefox浏览器对象。
  • driver.get():这是标准的Selenium方法,用于跳转到指定网址。
  • touch(Template(...)):这是Airtest的“灵魂”。Template对象封装了一张图片。touch命令会在当前屏幕上寻找与这张图片最匹配的区域,然后执行点击操作。如果只是定位(像搜索框那样),它会把光标移过去。
  • text():在当前位置输入文本。

关键步骤——截图

  1. 运行上述脚本前,先手动打开Firefox,访问www.baidu.com
  2. 使用系统自带的截图工具(如Windows的Snipping Tool),精确地截取搜索框的区域,尽量只包含输入框本身,背景干净。保存为search_box.png,放在你的Python脚本同一个文件夹里。
  3. 同样,截取“百度一下”按钮,保存为search_button.png

运行脚本:在命令行中,切换到你的脚本所在目录,输入python first_test.py。你会看到Firefox自动打开,访问百度,自动在搜索框输入文字并点击搜索。整个过程,我们一行HTML代码都没分析,全靠“看图操作”。

实操心得:图像识别的优势是直观、抗前端微小改动(比如CSS样式变了但按钮样子没变)。但它的缺点是速度相对慢一点,且受屏幕分辨率、缩放比例影响。截图时务必保证运行脚本时的浏览器状态(页面缩放比例、窗口大小)与截图时尽量一致。

4. 深入Selenium:元素定位与稳健操作

纯靠图像识别,遇到复杂逻辑或需要获取页面数据时就力不从心了。这时就需要祭出Selenium的看家本领——元素定位。Selenium提供了多达8种定位元素的方法,我们掌握最常用的3-4种就足以应对90%的场景。

让我们改造上面的脚本,用Selenium的方式实现同样的功能:

from airtest_selenium.proxy import WebFirefox from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time driver = WebFirefox() driver.maximize_window() driver.get("https://www.baidu.com") # 方法1:通过ID定位。查看百度首页搜索框的HTML,会发现它有 id="kw" search_box = driver.find_element(By.ID, "kw") # 找到元素后,清空(防止有默认文本),然后输入内容 search_box.clear() search_box.send_keys("自动化测试") # 方法2:通过CSS_SELECTOR定位“百度一下”按钮。它的id是“su” search_button = driver.find_element(By.CSS_SELECTOR, "#su") search_button.click() # 或者,方法3:在搜索框输入后直接按回车键,无需定位按钮 # search_box.send_keys(Keys.RETURN) time.sleep(5) driver.quit()

元素定位方法详解

  • By.ID:通过HTML元素的id属性定位。id通常是唯一的,定位速度最快,首选。
  • By.NAME:通过name属性定位。
  • By.CLASS_NAME:通过class属性定位。注意,一个元素可能有多个class。
  • By.CSS_SELECTOR:通过CSS选择器定位,功能非常强大灵活。#su表示选择id="su"的元素。
  • By.XPATH:通过XML路径定位,功能最强大但也最复杂,可以在没有id、class时使用,但性能稍差。

如何查看元素属性? 在浏览器页面按F12打开开发者工具,点击左上角的箭头图标,然后去点击页面上的元素(如搜索框),开发者工具就会自动定位到对应的HTML代码,里面就能看到idclassname等属性。

注意事项find_element只返回找到的第一个匹配元素。如果页面上有多个相同特征的元素,请使用find_elements(返回列表)或更精确的选择器。元素定位是自动化测试稳定的基石,定位不准,后续所有操作都会失败。

5. 混合模式实战:图像识别与代码控制的完美配合

纯粹的图像识别或纯粹的代码控制都不是最优解。真正的效率来自于“混合模式”:在常规流程中用稳定的Selenium代码,在那些难以定位、动态变化或者需要视觉确认的地方,用Airtest的图像识别来辅助。

场景:测试一个购物网站,需要登录。登录按钮很容易用By.ID定位。但登录后,页面上可能会弹出一个随机的、非HTML元素构成的图形验证码,或者一个动态的欢迎提示浮层。这时,Selenium就无能为力了。

混合模式脚本示例

from airtest_selenium.proxy import WebFirefox from selenium.webdriver.common.by import By from airtest.core.api import * import time driver = WebFirefox() driver.get("你的购物网站登录页URL") # --- 第一部分:使用Selenium完成稳定操作 --- # 定位用户名、密码输入框并输入 username = driver.find_element(By.ID, "username") password = driver.find_element(By.ID, "password") username.send_keys("your_username") password.send_keys("your_password") # 定位并点击登录按钮(假设是标准HTML按钮) login_button = driver.find_element(By.CSS_SELECTOR, ".login-btn") login_button.click() time.sleep(2) # 等待页面跳转或弹窗出现 # --- 第二部分:使用Airtest处理非标准或动态内容 --- # 情况1:处理图形验证码弹窗(假设弹窗关闭按钮是张图片) try: # 尝试用Selenium定位关闭按钮,如果找不到(可能是图片),则捕获异常 close_btn = driver.find_element(By.CLASS_NAME, "close-modal") close_btn.click() except: # 使用Airtest图像识别来点击关闭按钮的截图 # 你需要事先截好弹窗关闭按钮的图,保存为 close_popup.png touch(Template(r"close_popup.png")) print("使用图像识别关闭了弹窗。") # 情况2:登录成功后,需要视觉确认某个元素(如用户头像)出现 # 截图保存用户头像区域为 avatar.png if exists(Template(r"avatar.png")): print("登录成功,用户头像已显示!") else: print("登录可能失败,未检测到头像。") # 可以在这里加入失败处理逻辑,比如截图保存错误现场 driver.save_screenshot("login_failed.png") # --- 继续后续的Selenium自动化操作 --- # 例如,去搜索商品 search_input = driver.find_element(By.ID, "search") search_input.send_keys("手机") search_input.submit() time.sleep(3) driver.quit()

混合模式的优势

  1. 稳定性:核心流程(输入、点击标准按钮)用Selenium,不受UI样式微小变化影响。
  2. 灵活性:应对弹窗、验证码、Flash组件等“代码盲区”时,图像识别是最后的保障。
  3. 可维护性:大部分逻辑是结构化的代码,易于阅读和修改。图像识别作为补充,只在必要时使用。

实操心得:在项目中,我会为常见的非标准弹窗、广告位、验证码区域建立专门的“图片库”。当Selenium脚本运行失败时,会触发一个“图像救援”流程,尝试用预存的图片去识别并处理这些干扰项。这大大提高了自动化脚本的健壮性。

6. 核心技巧与最佳实践:让你的脚本更健壮

写一个能跑的脚本不难,写一个能在各种环境下稳定运行的脚本才是本事。下面这些技巧,是我在无数个调试的夜晚总结出来的。

6.1 智能等待:告别sleep的笨办法

新手最爱用time.sleep(10),这是万恶之源。网络慢一点、电脑卡一点,10秒可能不够;网络快,10秒又纯属浪费。Selenium提供了两种智能等待:

  • 隐式等待driver.implicitly_wait(10)。设置后,在查找任何元素时,如果立即没找到,WebDriver会轮询DOM最多10秒,一旦找到就继续。这是一个全局设置。
  • 显式等待:更精确、更推荐。它可以等待某个特定条件成立(如元素可见、可点击、包含特定文本)。
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待“百度一下”按钮可见并可点击,最多等10秒 wait = WebDriverWait(driver, 10) search_button = wait.until(EC.element_to_be_clickable((By.ID, "su"))) search_button.click()

最佳实践:在脚本开头设置一个较短的隐式等待(如5秒),作为安全网。在关键操作(如点击一个需要加载的按钮后跳转)前,使用显式等待,条件更精确。

6.2 异常处理与日志记录

脚本不可能永远成功。网络中断、元素变更、页面加载超时都会导致失败。好的脚本要能“优雅地失败”,并告诉你为什么。

import logging from selenium.common.exceptions import NoSuchElementException, TimeoutException logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') try: element = driver.find_element(By.ID, "nonExistentId") element.click() except NoSuchElementException: logging.error("未找到ID为'nonExistentId'的元素,页面结构可能已变更。") driver.save_screenshot("error_screenshot.png") # 保存现场截图 except TimeoutException: logging.warning("操作超时,请检查网络或服务器状态。") except Exception as e: logging.critical(f"发生未知错误: {e}") finally: # 无论成功失败,最后都尝试关闭浏览器 if driver: driver.quit()

6.3 页面对象模型(POM)设计模式初探

当测试用例越来越多,你会发现同样的元素定位代码散落在各个脚本里,一旦页面改了一个id,你需要修改所有脚本。POM就是为了解决这个问题:将页面元素定位和页面操作封装成单独的类

# login_page.py - 登录页面对象类 class LoginPage: def __init__(self, driver): self.driver = driver self.username_input = (By.ID, "username") self.password_input = (By.ID, "password") self.login_button = (By.CSS_SELECTOR, ".login-btn") def enter_username(self, username): self.driver.find_element(*self.username_input).send_keys(username) def enter_password(self, password): self.driver.find_element(*self.password_input).send_keys(password) def click_login(self): self.driver.find_element(*self.login_button).click() # 在你的测试脚本中 from login_page import LoginPage driver = WebFirefox() login_page = LoginPage(driver) driver.get("登录页URL") login_page.enter_username("test_user") login_page.enter_password("test_pass") login_page.click_login()

这样做的好处是,如果登录页面的id变了,你只需要修改LoginPage这个类里的一个地方,所有用到这个页面的测试脚本都自动生效。这是迈向编写可维护、大型自动化测试套件的重要一步。

7. 常见问题排查与调试技巧实录

即使按照教程一步步来,你也可能会遇到问题。下面是我整理的“故障排除清单”,覆盖了90%的常见错误。

问题现象可能原因解决方案
WebDriverException: Message: ‘geckodriver’ executable needs to be in PATH.系统找不到GeckoDriver驱动。1. 确认geckodriver文件已下载。2. 确认其所在目录已添加到系统环境变量PATH中,或将其放在Python安装目录下。3. 重启命令行终端。
SessionNotCreatedException: Unable to find a matching set of capabilities浏览器版本与驱动版本不匹配。检查Firefox浏览器版本,下载对应大版本号的GeckoDriver。去GeckoDriver官网查看版本支持表。
NoSuchElementException: Unable to locate element元素定位失败。1.检查定位器:用F12开发者工具确认元素的id/class/selector是否正确,注意是否在iframe内。2.检查时机:元素是否还没加载出来?在find_element前加入time.sleep显式等待。3.检查唯一性:定位器是否匹配了多个元素?find_element只取第一个。
ElementNotInteractableException: Element is not clickable at point元素不可交互。1. 元素被遮挡(如弹窗)。关闭遮挡物。2. 元素在视窗外。使用driver.execute_script(“arguments[0].scrollIntoView();”, element)滚动到元素位置。3. 元素状态为disabled。检查业务逻辑。
Airtest的touch()命令报错TargetNotFoundError未在屏幕上找到匹配的图片。1.截图问题:截图是否准确?背景是否太杂乱?运行脚本时,浏览器窗口位置、大小、页面缩放比例是否与截图时完全一致?2.阈值问题Template可以设置threshold(匹配阈值,默认0.8)。如果画面有微小变化,尝试降低阈值,如Template(r”img.png”, threshold=0.7)。3. 使用exists()函数先判断图片是否存在,再进行操作。
脚本在IDE里运行正常,在命令行运行失败环境变量或当前工作目录问题。1. 确保在命令行中,当前目录是你的脚本所在目录(尤其是涉及图片路径时)。2. 检查命令行中Python和pip的版本是否与IDE中使用的一致。
Firefox启动后马上闪退可能是浏览器驱动与浏览器版本严重不兼容,或存在多个Firefox实例冲突。1. 彻底关闭所有Firefox进程(在任务管理器中结束firefox.exe)。2. 使用driver = WebFirefox(firefox_binary=’Firefox可执行文件路径’)显式指定浏览器位置。

调试技巧

  • 多用screenshot:在关键步骤前后,特别是失败前,使用driver.save_screenshot(“step1.png”)保存截图,能帮你直观看到当时的页面状态。
  • 打印信息:多用print()输出当前URL、元素属性等信息。
  • 手动复现:遇到问题时,尝试手动操作一遍,观察页面是否有异步加载、弹窗、重定向等意外行为。
  • 降低速度:在开发调试阶段,可以在操作之间加入time.sleep(1),让你能看清自动化过程,便于定位问题。

走到这里,你已经从一个对自动化测试一无所知的新手,变成了一个能够搭建环境、编写混合模式脚本、并具备基本排查能力的实践者。这套Airtest-Selenium的组合,为你打开了一扇门。接下来,你可以尝试用它们去自动化你日常工作中那些重复的网页操作,比如数据抓取、报表生成、定期巡检等。记住,所有复杂的自动化项目,都是由一个个像“打开百度并搜索”这样的小脚本组合而成的。