Selenium 多窗口截图(窗口切换)二次封装方法详解 —— Python 实践

发布于:2025-06-29 ⋅ 阅读:(18) ⋅ 点赞:(0)

        在Web自动化测试过程中,经常会遇到点击链接或按钮后打开新窗口的情况。为了验证新窗口是否成功加载,并确保测试流程顺利进行,我们需要对新窗口进行截图操作
        然而,直接使用 time.sleep() 等待新窗口加载完成并不稳定,容易导致截图失败或截取到旧页面内容。因此,本文将详细介绍如何通过 显式等待 + 多窗口切换 + 截图封装 的方式,实现一个 健壮、可复用的多窗口截图解决方案。

🧾  一、项目背景

在自动化测试中,经常遇到如下场景:

  • 点击一个链接或按钮后,浏览器会打开一个新的页签(Tab)。
  • 测试需要切换到新页签并执行后续操作(如断言、截图等)。
  • 如果不进行适当的等待和切换,截图可能会失败或者截取到错误页面的内容。

🧱 二、核心思路

要实现多窗口截图功能,我们需要以下几个步骤:

  1. 触发新窗口打开:通过点击某个元素,打开新的页签。
  2. 等待新窗口出现:使用 WebDriverWait 显式等待新窗口句柄出现。
  3. 切换到新窗口:获取所有窗口句柄,切换到最后一个窗口。
  4. 等待页面加载完成:等待某个关键元素可见,确保页面内容加载完毕。
  5. 执行截图操作:调用截图方法保存新窗口内容。

🛠️ 三、代码封装

我们将上述逻辑封装成一个通用的方法,便于在多个测试用例中复用。

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 Logger


class 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_handles
        self.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() 生成唯一文件名,避免命名冲突。
  • 日志输出截图路径,便于调试和验证。

📝 五、总结

通过将多窗口截图逻辑封装为基类方法,我们可以:

  • 提高代码复用率,减少重复代码;
  • 提升测试稳定性,避免因页面加载未完成导致的截图失败;
  • 统一截图命名和存储路径,便于后期分析和报告展示;
  • 使测试逻辑更清晰,易于维护和扩展。

网站公告

今日签到

点亮在社区的每一天
去签到