Selenium 多窗口截图(窗口切换)二次封装方法详解 —— Python 实践
在Web自动化测试过程中,经常会遇到点击链接或按钮后打开新窗口的情况。为了验证新窗口是否成功加载,并确保测试流程顺利进行,我们需要对新窗口进行截图操作。
然而,直接使用 time.sleep() 等待新窗口加载完成并不稳定,容易导致截图失败或截取到旧页面内容。因此,本文将详细介绍如何通过 显式等待 + 多窗口切换 + 截图封装 的方式,实现一个 健壮、可复用的多窗口截图解决方案。
🧾 一、项目背景
在自动化测试中,经常遇到如下场景:
- 点击一个链接或按钮后,浏览器会打开一个新的页签(Tab)。
- 测试需要切换到新页签并执行后续操作(如断言、截图等)。
- 如果不进行适当的等待和切换,截图可能会失败或者截取到错误页面的内容。
🧱 二、核心思路
要实现多窗口截图功能,我们需要以下几个步骤:
- 触发新窗口打开:通过点击某个元素,打开新的页签。
- 等待新窗口出现:使用 WebDriverWait 显式等待新窗口句柄出现。
- 切换到新窗口:获取所有窗口句柄,切换到最后一个窗口。
- 等待页面加载完成:等待某个关键元素可见,确保页面内容加载完毕。
- 执行截图操作:调用截图方法保存新窗口内容。
🛠️ 三、代码封装
我们将上述逻辑封装成一个通用的方法,便于在多个测试用例中复用。
1. 在 TestBase 基类中添加方法
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time
import os
import logging
from utils.logger import Loggerclass TestBase(unittest.TestCase):def wait_for_new_window(self, timeout=10):"""等待新窗口出现:param timeout: 最大等待时间(秒)"""WebDriverWait(self.driver, timeout).until(lambda driver: len(driver.window_handles) > 1)def switch_to_new_window(self):"""切换到最新打开的窗口"""window_handles = self.driver.window_handlesself.driver.switch_to.window(window_handles[-1])def wait_for_page_load(self, locator, timeout=10):"""等待页面某个元素加载完成:param locator: 元素定位器 (By, value):param timeout: 最大等待时间(秒)"""WebDriverWait(self.driver, timeout).until(EC.visibility_of_element_located(locator))def get_browser_img(self, filename=None):"""截图并保存到项目根目录下的 screenshots 文件夹中:param filename: 自定义文件名(可选)"""base_dir = os.path.dirname(os.path.abspath(__file__)) # 获取当前文件路径screenshot_dir = os.path.join(base_dir, 'screenshots') # 构建截图目录路径# 如果目录不存在,则自动创建os.makedirs(screenshot_dir, exist_ok=True)# 生成时间戳文件名if not filename:timestamp = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))filename = f"{timestamp}.png"screen_path = os.path.join(screenshot_dir, filename)try:self.driver.get_screenshot_as_file(screen_path)logger.info(f"截图已保存至: {screen_path}")except Exception as e:logger.error(f"截图失败!{e}")
2. 使用示例:在测试用例中调用
假设我们有一个测试用例,点击百度热搜第一条后跳转到新页面并截图。
def test_baidu_search_link(self):input = Cloud(self.driver)input.open('https://www.baidu.com/')input.click_link() # 点击热搜链接,打开新页签self.wait_for_new_window(10) # 等待新窗口打开self.switch_to_new_window() # 切换到新窗口self.wait_for_page_load((By.TAG_NAME, 'body'), 10) # 等待页面 body 加载完成input.get_browser_img() # 执行截图
📦 四、关键点说明
✅ 显式等待 vs 隐式等待
- 显式等待(WebDriverWait)是推荐的做法,因为它会等待特定条件成立后再继续执行,避免因网络延迟或页面加载慢导致的问题。
- 隐式等待(implicitly_wait)虽然可以全局设置,但无法精准控制等待条件。
✅ 窗口切换逻辑
- 使用 window_handles 获取所有窗口句柄列表。
- 使用 switch_to.window() 切换到目标窗口。
- 推荐切换到最后一个窗口(window_handles[-1]),即最新打开的窗口。
✅ 截图路径管理
- 使用 os.makedirs(..., exist_ok=True) 自动创建截图目录。
- 使用 time.strftime() 生成唯一文件名,避免命名冲突。
- 日志输出截图路径,便于调试和验证。
📝 五、总结
通过将多窗口截图逻辑封装为基类方法,我们可以:
- 提高代码复用率,减少重复代码;
- 提升测试稳定性,避免因页面加载未完成导致的截图失败;
- 统一截图命名和存储路径,便于后期分析和报告展示;
- 使测试逻辑更清晰,易于维护和扩展。