06.浏览器窗口的句柄和多窗口切换

06.浏览器窗口的句柄和多窗口切换

什么是窗口句柄(Window Handle)

窗口句柄是浏览器为每个打开的窗口或标签页分配的唯一标识符。在Selenium中,我们需要通过这个句柄来识别和切换不同的窗口。

句柄的特点:

  • 每个窗口都有唯一的句柄ID
  • 句柄是字符串类型
  • 句柄在窗口关闭前保持不变
  • 无法预测句柄的具体值

获取窗口句柄的方法

1. 获取当前窗口句柄

// 获取当前活动窗口的句柄
String currentWindowHandle = driver.getWindowHandle();
System.out.println("当前窗口句柄: " + currentWindowHandle);

2. 获取所有窗口句柄

// 获取所有打开窗口的句柄集合
Set<String> allWindowHandles = driver.getWindowHandles();
System.out.println("所有窗口句柄数量: " + allWindowHandles.size());

// 遍历所有句柄
for (String handle : allWindowHandles) {
    System.out.println("窗口句柄: " + handle);
}

窗口切换的基本方法

使用switchTo().window()方法

// 切换到指定句柄的窗口
driver.switchTo().window(windowHandle);

多窗口切换的实际应用场景

场景1:处理新打开的窗口

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.By;
import java.util.Set;

public class WindowSwitchExample {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();

        try {
            // 打开主页面
            driver.get("https://example.com");

            // 获取主窗口句柄
            String mainWindowHandle = driver.getWindowHandle();
            System.out.println("主窗口句柄: " + mainWindowHandle);

            // 点击链接打开新窗口(假设这个链接会在新窗口打开)
            driver.findElement(By.linkText("打开新窗口")).click();

            // 等待新窗口打开
            Thread.sleep(2000);

            // 获取所有窗口句柄
            Set<String> allHandles = driver.getWindowHandles();

            // 切换到新窗口
            for (String handle : allHandles) {
                if (!handle.equals(mainWindowHandle)) {
                    driver.switchTo().window(handle);
                    System.out.println("已切换到新窗口: " + handle);
                    break;
                }
            }

            // 在新窗口中执行操作
            System.out.println("新窗口标题: " + driver.getTitle());

            // 切换回主窗口
            driver.switchTo().window(mainWindowHandle);
            System.out.println("已切换回主窗口");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            driver.quit();
        }
    }
}

场景2:处理多个窗口的切换

public class MultipleWindowsExample {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();

        try {
            // 打开主页面
            driver.get("https://example.com");
            String mainWindow = driver.getWindowHandle();

            // 打开多个新窗口
            for (int i = 1; i <= 3; i++) {
                // 使用JavaScript打开新窗口
                ((JavascriptExecutor) driver).executeScript("window.open('https://example" + i + ".com', '_blank');");
                Thread.sleep(1000);
            }

            // 获取所有窗口句柄
            Set<String> allWindows = driver.getWindowHandles();
            System.out.println("总共打开了 " + allWindows.size() + " 个窗口");

            // 遍历所有窗口并执行操作
            int windowIndex = 0;
            for (String windowHandle : allWindows) {
                driver.switchTo().window(windowHandle);
                System.out.println("窗口 " + windowIndex + " 标题: " + driver.getTitle());
                System.out.println("窗口 " + windowIndex + " URL: " + driver.getCurrentUrl());
                windowIndex++;
            }

            // 切换回主窗口
            driver.switchTo().window(mainWindow);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            driver.quit();
        }
    }
}

实用的窗口管理工具方法

1. 切换到最新打开的窗口

public static void switchToLatestWindow(WebDriver driver) {
    Set<String> handles = driver.getWindowHandles();
    String latestHandle = "";
    for (String handle : handles) {
        latestHandle = handle;
    }
    driver.switchTo().window(latestHandle);
}

2. 根据窗口标题切换

public static boolean switchToWindowByTitle(WebDriver driver, String expectedTitle) {
    Set<String> handles = driver.getWindowHandles();
    for (String handle : handles) {
        driver.switchTo().window(handle);
        if (driver.getTitle().contains(expectedTitle)) {
            return true;
        }
    }
    return false;
}

3. 根据URL切换窗口

public static boolean switchToWindowByUrl(WebDriver driver, String expectedUrl) {
    Set<String> handles = driver.getWindowHandles();
    for (String handle : handles) {
        driver.switchTo().window(handle);
        if (driver.getCurrentUrl().contains(expectedUrl)) {
            return true;
        }
    }
    return false;
}

4. 关闭除主窗口外的所有窗口

public static void closeAllWindowsExceptMain(WebDriver driver, String mainWindowHandle) {
    Set<String> handles = driver.getWindowHandles();
    for (String handle : handles) {
        if (!handle.equals(mainWindowHandle)) {
            driver.switchTo().window(handle);
            driver.close();
        }
    }
    driver.switchTo().window(mainWindowHandle);
}

完整的多窗口处理示例

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import java.util.Set;

public class CompleteWindowHandlingExample {
    private WebDriver driver;
    private String mainWindowHandle;

    public void setUp() {
        driver = new ChromeDriver();
        driver.manage().window().maximize();
    }

    public void testMultipleWindows() {
        try {
            // 1. 打开主页面
            driver.get("https://www.baidu.com");
            mainWindowHandle = driver.getWindowHandle();
            System.out.println("主窗口句柄: " + mainWindowHandle);

            // 2. 打开新窗口
            ((JavascriptExecutor) driver).executeScript("window.open('https://www.google.com', '_blank');");
            Thread.sleep(2000);

            // 3. 获取所有窗口句柄
            Set<String> allHandles = driver.getWindowHandles();
            System.out.println("当前打开窗口数量: " + allHandles.size());

            // 4. 切换到新窗口
            for (String handle : allHandles) {
                if (!handle.equals(mainWindowHandle)) {
                    driver.switchTo().window(handle);
                    System.out.println("切换到新窗口,标题: " + driver.getTitle());
                    break;
                }
            }

            // 5. 在新窗口中执行操作
            driver.findElement(By.name("q")).sendKeys("Selenium WebDriver");
            Thread.sleep(2000);

            // 6. 再打开一个窗口
            ((JavascriptExecutor) driver).executeScript("window.open('https://www.github.com', '_blank');");
            Thread.sleep(2000);

            // 7. 切换到GitHub窗口
            switchToWindowByUrl(driver, "github.com");
            System.out.println("当前窗口标题: " + driver.getTitle());

            // 8. 显示所有窗口信息
            displayAllWindowsInfo();

            // 9. 关闭当前窗口
            driver.close();

            // 10. 切换回主窗口
            driver.switchTo().window(mainWindowHandle);
            System.out.println("已切换回主窗口: " + driver.getTitle());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void displayAllWindowsInfo() {
        Set<String> handles = driver.getWindowHandles();
        String currentHandle = driver.getWindowHandle();

        System.out.println("n=== 所有窗口信息 ===");
        int index = 1;
        for (String handle : handles) {
            driver.switchTo().window(handle);
            System.out.println("窗口 " + index + ":");
            System.out.println("  句柄: " + handle);
            System.out.println("  标题: " + driver.getTitle());
            System.out.println("  URL: " + driver.getCurrentUrl());
            index++;
        }

        // 切换回原来的窗口
        driver.switchTo().window(currentHandle);
    }

    private boolean switchToWindowByUrl(WebDriver driver, String expectedUrl) {
        Set<String> handles = driver.getWindowHandles();
        for (String handle : handles) {
            driver.switchTo().window(handle);
            if (driver.getCurrentUrl().contains(expectedUrl)) {
                return true;
            }
        }
        return false;
    }

    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    public static void main(String[] args) {
        CompleteWindowHandlingExample example = new CompleteWindowHandlingExample();
        example.setUp();
        example.testMultipleWindows();
        example.tearDown();
    }
}

常见问题和注意事项

1. 窗口句柄的获取时机

// ❌ 错误:在新窗口打开前就获取句柄
Set<String> handles = driver.getWindowHandles();
driver.findElement(By.linkText("新窗口")).click();

// ✅ 正确:在新窗口打开后获取句柄
driver.findElement(By.linkText("新窗口")).click();
Thread.sleep(2000); // 等待新窗口打开
Set<String> handles = driver.getWindowHandles();

2. 窗口切换后的状态确认

// 切换窗口后确认切换成功
driver.switchTo().window(targetHandle);
System.out.println("当前窗口标题: " + driver.getTitle());
System.out.println("当前窗口URL: " + driver.getCurrentUrl());

3. 处理窗口关闭

// 关闭当前窗口
driver.close();

// 如果关闭的是最后一个窗口,需要重新获取句柄
Set<String> remainingHandles = driver.getWindowHandles();
if (!remainingHandles.isEmpty()) {
    driver.switchTo().window(remainingHandles.iterator().next());
}

4. 等待新窗口打开

// 使用显式等待等待新窗口打开
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.numberOfWindowsToBe(2));

最佳实践

  1. 保存主窗口句柄:在测试开始时保存主窗口句柄,便于后续切换回来
  2. 使用有意义的变量名:给窗口句柄使用描述性的变量名
  3. 及时关闭不需要的窗口:避免打开过多窗口影响性能
  4. 异常处理:在窗口操作中添加适当的异常处理
  5. 等待机制:使用适当的等待确保窗口完全加载

总结

窗口句柄和多窗口切换是Selenium自动化测试中的重要概念。掌握这些技能可以帮助您:

  • 处理复杂的多窗口场景
  • 在不同窗口间自由切换
  • 管理和控制浏览器窗口
  • 提高测试脚本的稳定性和可靠性

记住:每次窗口切换后,所有后续的元素操作都会在当前活动窗口中执行。

发表评论