Python 自动化测试开发教程:Selenium 从入门到实战(1)
Selenium 是目前最流行的 Web 自动化测试工具之一,它可以模拟用户在浏览器中的各种操作,实现 Web 应用的自动化测试。本教程将带你从零基础开始,逐步掌握 Selenium 的核心用法和实战技巧。
一、环境搭建
1. 安装 Selenium
首先需要安装 Selenium 库:
pip install selenium
2. 安装浏览器驱动
Selenium 需要浏览器驱动才能控制浏览器,不同浏览器需要对应版本的驱动:
- Chrome: ChromeDriver
- Firefox: GeckoDriver
- Edge: EdgeDriver
安装步骤:
- 下载与你的浏览器版本匹配的驱动
- 将驱动文件所在目录添加到系统 PATH 环境变量
- 验证安装:在命令行输入驱动名称(如 chromedriver),能看到版本信息即安装成功
二、Selenium 基础用法
1. 第一个 Selenium 程序
下面是一个简单的示例,演示如何打开浏览器、访问网页并获取页面标题:
from selenium import webdriver
from selenium.webdriver.common.by import By# 初始化浏览器驱动(这里使用Chrome)
driver = webdriver.Chrome()# 打开网页
driver.get("https://www.baidu.com")# 打印页面标题
print("页面标题:", driver.title)# 查找搜索框并输入内容
search_box = driver.find_element(By.ID, "kw")
search_box.send_keys("Selenium 教程")# 查找搜索按钮并点击
search_button = driver.find_element(By.ID, "su")
search_button.click()# 等待3秒,让页面加载完成
import time
time.sleep(3)# 关闭浏览器
driver.quit()
2. 元素定位方法
Selenium 提供了多种元素定位方式,常用的有:
from selenium import webdriver
from selenium.webdriver.common.by import Bydriver = webdriver.Chrome()
driver.get("https://example.com")# 1. 通过ID定位
element = driver.find_element(By.ID, "some-id")# 2. 通过Name定位
element = driver.find_element(By.NAME, "some-name")# 3. 通过Class Name定位
element = driver.find_element(By.CLASS_NAME, "some-class")# 4. 通过Tag Name定位
element = driver.find_element(By.TAG_NAME, "div")# 5. 通过Link Text定位(精确匹配)
element = driver.find_element(By.LINK_TEXT, "点击这里")# 6. 通过Partial Link Text定位(部分匹配)
element = driver.find_element(By.PARTIAL_LINK_TEXT, "点击")# 7. 通过CSS选择器定位
element = driver.find_element(By.CSS_SELECTOR, "div.some-class > p#some-id")# 8. 通过XPath定位
element = driver.find_element(By.XPATH, "//div[@class='some-class']/p")# 查找多个元素(返回列表)
elements = driver.find_elements(By.TAG_NAME, "a")
print(f"找到{len(elements)}个链接")driver.quit()
定位技巧:
- 优先使用 ID(唯一且高效)
- 其次考虑 Name 或 Class Name
- 复杂场景使用 CSS 选择器或 XPath
- XPath 功能强大但性能相对较差
3. 常用元素操作
Selenium 提供了丰富的方法来操作页面元素:
from selenium import webdriver
from selenium.webdriver.common.by import By
import timedriver = webdriver.Chrome()
driver.get("https://www.example.com/form")# 文本输入框操作
input_field = driver.find_element(By.ID, "username")
input_field.clear() # 清空输入框
input_field.send_keys("testuser") # 输入文本
print("输入框内容:", input_field.get_attribute("value")) # 获取输入内容# 按钮操作
submit_button = driver.find_element(By.ID, "submit-btn")
submit_button.click() # 点击按钮
# submit_button.submit() # 表单提交(仅适用于表单元素)# 复选框操作
checkbox = driver.find_element(By.ID, "agree-terms")
if not checkbox.is_selected(): # 判断是否选中checkbox.click() # 选中复选框# 下拉菜单操作
from selenium.webdriver.support.ui import Select
select_element = Select(driver.find_element(By.ID, "country"))
select_element.select_by_visible_text("China") # 通过可见文本选择
# select_element.select_by_value("cn") # 通过value属性选择
# select_element.select_by_index(2) # 通过索引选择# 获取元素信息
link = driver.find_element(By.LINK_TEXT, "关于我们")
print("链接文本:", link.text) # 获取元素文本
print("链接地址:", link.get_attribute("href")) # 获取属性值
print("元素是否可见:", link.is_displayed()) # 判断元素是否可见time.sleep(2)
driver.quit()
三、等待机制
在 Web 自动化中,页面元素加载需要时间,直接操作可能导致元素未找到错误。Selenium 提供了三种等待机制:
1. 隐式等待
设置全局等待时间,对所有元素查找操作生效:
driver = webdriver.Chrome()
driver.implicitly_wait(10) # 隐式等待10秒
2. 显式等待
针对特定元素设置等待条件和超时时间:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutExceptiondriver = webdriver.Chrome()
driver.get("https://www.example.com/ajax-content")try:# 等待元素出现,最多等10秒,每500毫秒检查一次element = WebDriverWait(driver, 10, 0.5).until(EC.presence_of_element_located((By.ID, "dynamic-content")))print("找到动态加载的元素:", element.text)# 其他常用条件# EC.visibility_of_element_located: 元素可见# EC.element_to_be_clickable: 元素可点击# EC.text_to_be_present_in_element: 元素包含特定文本except TimeoutException:print("超时:未找到元素")
finally:driver.quit()
3. 强制等待
直接使用 time.sleep()
暂停执行,不推荐频繁使用:
import time
time.sleep(3) # 强制等待3秒
四、浏览器控制
Selenium 可以控制浏览器的各种行为:
from selenium import webdriver
import timedriver = webdriver.Chrome()# 打开网页
driver.get("https://www.baidu.com")# 窗口操作
driver.maximize_window() # 最大化窗口
# driver.set_window_size(1024, 768) # 设置窗口大小
# driver.set_window_position(100, 100) # 设置窗口位置# 页面操作
driver.get("https://www.google.com") # 访问新页面
driver.back() # 后退到上一页
time.sleep(1)
driver.forward() # 前进到下一页
time.sleep(1)
driver.refresh() # 刷新页面# 处理多窗口
original_window = driver.current_window_handle # 记录当前窗口
driver.find_element(By.LINK_TEXT, "新窗口打开").click() # 打开新窗口# 切换到新窗口
for window_handle in driver.window_handles:if window_handle != original_window:driver.switch_to.window(window_handle)breakprint("新窗口标题:", driver.title)# 关闭当前窗口
driver.close()# 切回原始窗口
driver.switch_to.window(original_window)# 页面截图
driver.save_screenshot("screenshot.png") # 保存截图# 获取页面信息
print("页面标题:", driver.title)
print("当前URL:", driver.current_url)
# print("页面源码:", driver.page_source) # 获取页面HTML源码time.sleep(2)
driver.quit() # 关闭所有窗口并退出
五、实战案例:用户注册功能测试
下面是一个完整的用户注册功能测试案例,结合了 pytest 框架:
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import random
import stringclass TestUserRegistration:"""用户注册功能测试类"""def setup_class(self):"""所有测试开始前执行一次"""self.driver = webdriver.Chrome()self.driver.implicitly_wait(10)self.driver.maximize_window()self.registration_url = "https://example.com/register"def teardown_class(self):"""所有测试结束后执行一次"""self.driver.quit()def setup_method(self):"""每个测试方法开始前执行"""self.driver.get(self.registration_url)def generate_random_email(self):"""生成随机邮箱地址"""random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))return f"test_{random_str}@example.com"def test_successful_registration(self):"""测试成功注册流程"""# 填写注册表单self.driver.find_element(By.ID, "username").send_keys("testuser123")self.driver.find_element(By.ID, "email").send_keys(self.generate_random_email())self.driver.find_element(By.ID, "password").send_keys("Test@123456")self.driver.find_element(By.ID, "confirm_password").send_keys("Test@123456")# 同意条款并提交self.driver.find_element(By.ID, "agree_terms").click()self.driver.find_element(By.ID, "register_btn").click()# 验证注册成功success_message = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, "success-message")))assert "注册成功" in success_message.textassert "/dashboard" in self.driver.current_urldef test_password_mismatch(self):"""测试密码不匹配的情况"""# 填写表单,使密码和确认密码不一致self.driver.find_element(By.ID, "username").send_keys("testuser456")self.driver.find_element(By.ID, "email").send_keys(self.generate_random_email())self.driver.find_element(By.ID, "password").send_keys("Test@123456")self.driver.find_element(By.ID, "confirm_password").send_keys("Different@123")self.driver.find_element(By.ID, "agree_terms").click()self.driver.find_element(By.ID, "register_btn").click()# 验证错误提示error_message = self.driver.find_element(By.ID, "password_error")assert "密码不一致" in error_message.textassert self.registration_url in self.driver.current_urldef test_empty_required_fields(self):"""测试必填字段为空的情况"""# 不填写必填字段,直接提交self.driver.find_element(By.ID, "register_btn").click()# 验证错误提示errors = self.driver.find_elements(By.CLASS_NAME, "error")assert len(errors) >= 3 # 至少有3个必填字段错误assert "用户名不能为空" in [e.text for e in errors]if __name__ == "__main__":pytest.main(["-v", "test_registration.py"])
六、高级技巧:Page Object 模式
Page Object 模式是一种设计模式,将页面元素和操作封装到类中,提高代码复用性和可维护性:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ECclass BasePage:"""基础页面类,封装通用方法"""def __init__(self, driver):self.driver = driverself.base_url = "https://example.com"def find_element(self, locator, timeout=10):"""查找单个元素"""return WebDriverWait(self.driver, timeout).until(EC.presence_of_element_located(locator))def find_elements(self, locator, timeout=10):"""查找多个元素"""return WebDriverWait(self.driver, timeout).until(EC.presence_of_all_elements_located(locator))def click(self, locator, timeout=10):"""点击元素"""self.find_element(locator, timeout).click()def input_text(self, locator, text, timeout=10):"""在输入框中输入文本"""element = self.find_element(locator, timeout)element.clear()element.send_keys(text)def get_text(self, locator, timeout=10):"""获取元素文本"""return self.find_element(locator, timeout).textdef navigate_to(self, path):"""导航到指定路径"""self.driver.get(f"{self.base_url}{path}")class LoginPage(BasePage):"""登录页面类"""# 元素定位器USERNAME_INPUT = (By.ID, "username")PASSWORD_INPUT = (By.ID, "password")LOGIN_BUTTON = (By.ID, "login_btn")ERROR_MESSAGE = (By.CLASS_NAME, "error-message")def __init__(self, driver):super().__init__(driver)self.path = "/login"def load(self):"""加载登录页面"""self.navigate_to(self.path)def login(self, username, password):"""执行登录操作"""self.input_text(self.USERNAME_INPUT, username)self.input_text(self.PASSWORD_INPUT, password)self.click(self.LOGIN_BUTTON)def get_error_message(self):"""获取错误消息"""return self.get_text(self.ERROR_MESSAGE)class DashboardPage(BasePage):"""仪表盘页面类"""# 元素定位器WELCOME_MESSAGE = (By.CLASS_NAME, "welcome-message")LOGOUT_BUTTON = (By.ID, "logout_btn")def __init__(self, driver):super().__init__(driver)self.path = "/dashboard"def get_welcome_message(self):"""获取欢迎消息"""return self.get_text(self.WELCOME_MESSAGE)def logout(self):"""执行登出操作"""self.click(self.LOGOUT_BUTTON)# 使用示例
if __name__ == "__main__":driver = webdriver.Chrome()driver.maximize_window()# 登录流程login_page = LoginPage(driver)login_page.load()login_page.login("testuser", "testpassword")# 验证登录成功dashboard_page = DashboardPage(driver)assert "欢迎回来,testuser" in dashboard_page.get_welcome_message()# 登出dashboard_page.logout()driver.quit()
七、测试报告与持续集成
1. 生成测试报告
使用 pytest-html 插件生成美观的 HTML 报告:
pip install pytest-html
pytest test_registration.py --html=report.html
2. 持续集成配置
在 CI 环境(如 GitHub Actions)中运行 Selenium 测试:
# .github/workflows/selenium-tests.yml
name: Selenium Testson: [push, pull_request]jobs:test:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- name: Set up Pythonuses: actions/setup-python@v2with:python-version: '3.9'- name: Install dependenciesrun: |python -m pip install --upgrade pippip install -r requirements.txt- name: Run testsrun: |pytest --html=report.htmlenv:DISPLAY: :99- name: Upload reportuses: actions/upload-artifact@v2if: always()with:name: test-reportpath: report.html
八、常见问题与解决方案
-
元素定位不稳定
解决方案:使用显式等待代替隐式等待、避免使用动态变化的属性(如随机生成的 ID) -
浏览器兼容性问题
- 解决方案:在不同浏览器中运行测试
- 使用 WebDriverManager 自动管理驱动版本
-
测试执行速度慢
- 解决方案:使用 pytest-xdist 并行执行测试
- 减少不必要的强制等待
-
验证码处理
- 解决方案:测试环境中关闭验证码
- 使用 API 获取测试验证码
- 与开发团队合作添加测试专用绕过机制
总结
Selenium 是一个功能强大的 Web 自动化测试工具,通过本教程你应该已经掌握了其核心用法:
- 环境搭建与基础配置
- 元素定位与操作方法
- 等待机制的合理使用
- 浏览器控制技巧
- 测试框架整合与 Page Object 模式
- 测试报告与持续集成
自动化测试是一个持续优化的过程,建议在实际项目中不断总结经验,编写可维护、可扩展的测试代码。随着实践的深入,你还可以探索 Selenium Grid 实现分布式测试,以及与其他工具(如 Appium)结合进行移动端测试。
祝你在自动化测试的道路上越走越远!