自动化测试-多窗口处理 + frame处理

文章目录

✨✨✨学习的道路很枯燥,希望我们能并肩走下来!

编程真是一件很奇妙的东西。你只是浅尝辄止,那么只会觉得枯燥乏味,像对待任务似的应付它。但你如果深入探索,就会发现其中的奇妙,了解许多所不知道的原理。知识的力量让你沉醉,甘愿深陷其中并发现宝藏。



本文开始

1. 为什么要切换窗口?

问题 :直接定位一个重新打开的新窗口的元素,可以定位到吗?
答案 :不能直接定位新窗口元素;
切换原因 : WebDriver的设计原理,driver一次只能连接一个页面的DOM, 当遇到不同的页面,需要切换窗口,也就是切换句柄;

句柄:每个页面的唯一标识/标签;-每次获取句柄都会变化;

切换原因解释:

xml 复制代码
// WebDriver 设计原理:一次只能连接一个页面的DOM
WebDriver driver = new ChromeDriver();

// 初始状态:连接到窗口A的DOM
// 内存中的结构:
// WebDriver实例 → 窗口A的DOM树

// 新窗口打开后:
// 物理上:窗口A(旧)  窗口B(新)
// WebDriver:仍然连接到窗口A的DOM树 ← 驱动只能看到这个!

// 尝试定位时:
driver.findElement(By.id("newWindowElement"));
// WebDriver只在窗口A的DOM树中搜索
// 窗口B的元素根本不在这个DOM树中
xml 复制代码
浏览器实际状态:在窗口A打开了窗口B
┌─────────────────┐    ┌─────────────────┐
│  窗口A (旧)      │    │  窗口B (新)      │
│                 │    │                 │
│ <div id="old">  │    │ <div id="new">  │
│  旧内容          │    │  新内容          │
│ </div>          │    │ </div>          │
└─────────────────┘    └─────────────────┘
      ↑                        ↑
      │                        │
      └────────────────────────┘
             用户能看到两个窗口

WebDriver视角:句柄没变只能看到窗口A
┌─────────────────┐
│  窗口A的DOM树     │ ← WebDriver唯一能看到的地方
│                 │
│ <div id="old">  │
│  旧内容          │
│ </div>          │
└─────────────────┘

// 当你想要"定位新窗口元素"时:
// WebDriver说:"找不到元素"

多窗口介绍:

=》切换窗口原因:在一个浏览器中同时打开多个窗口或选项卡。

=》如果不进行窗口切换,就无法在新窗口中进行后续的操作。

特殊情况:异步刷新不需要切换句柄

异步刷新和新窗口的区别:

1)异步刷新:更新当前页面的DOM(同一个document对象)

2)新窗口:加载全新的HTML文档(不同的document对象)

判断 异步刷新 和 新窗口页面方式如下:

2. 多窗口处理

多窗口处理逻辑:

1)获取当前窗口句柄;

2)获取所有窗口句柄;

3)判断是否是想要操作的窗口?

是想要操作的窗口:直接对窗口操作

不是想要操作的窗口:切换窗口,再对新窗口操作

getWindowHandles() 确实能获取到所有打开的页面句柄,包括标签页、弹出窗口等。

大致逻辑如下

java 复制代码
// 标准使用模式
String mainWindow = driver.getWindowHandle();  // 1. 保存原窗口
// 触发打开新窗口的操作...
Set<String> allWindows = driver.getWindowHandles();  // 2. 获取所有句柄
// 切换到新窗口操作...
driver.switchTo().window(mainWindow);  // 3. 切回原窗口

小练习代码演示:

java 复制代码
	/**
     * 多窗口切换小练习
     *
     */
    @Test
    public void twoHandleSwitch() {
        List<Executable> executableList = new ArrayList<>();
        //1. 获取驱动
        WebDriver driver = WebDriverManager.chromedriver().create();
        //2. 声明隐式等待
        webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        //3. 打开要操作的页面 URL
        String url = "https://study_up_up/frame";
        webDriver.get(url);
        //4. 获取当前页面句柄
        String originalWindow = webDriver.getWindowHandle();
        System.out.println("第一个页面句柄:" + originalWindow);
        String title1 = webDriver.getTitle();
        System.out.println("最初页面标题=:" + title1);
        //断言-当前页面标题是否包含测试
        executableList.add(() -> assertTrue(title1.contains("测试")));
        //5. 进行业务逻辑操作,打开一个新窗口
        WebElement element = webDriver.findElement(By.xpath("//*[text()='元素定位']"));
        element.click();//点击元素,进入新页面
        //6. 获取所有窗口句柄-能获取当前 WebDriver 会话管理的所有窗口/标签页句柄。
        //所有句柄=通过driver所有打开的窗口
        Set<String> windowHandles = webDriver.getWindowHandles();
        //7. 判断窗口是否需要切换,如果需要就直接切换
        for(String windowHandle : windowHandles) {
        	//判断不是想要操作的页面,就切换窗口
            if(!windowHandle.equals(originalWindow)) {
                webDriver.switchTo().window(windowHandle);
                break;
            }
        }
        //8. 新窗口业务逻辑
        String title2 = webDriver.getTitle();
        //判断页面标题包含测试
        executableList.add(() -> {assertThat(title2, containsString("测试"));});

        //9. 切回默认页面
        webDriver.switchTo().window(originalWindow);
        String title3 = webDriver.getTitle();
        System.out.println("title3: " + title3);

        //10.断言
        assertAll(executableList);
    }

【注】
为什么使用Set存储所有句柄呢?:

  1. 无序(不保证顺序)
  2. 不允许重复元素
  3. 快速查找

为什么使用WebDriverManager创建驱动呢?

简洁,一行代码配置驱动,没有对应浏览器驱动会自动配置;

自动下载和管理 ChromeDriver + 创建 ChromeDriver 实例;

setup() + new ChromeDriver() = create();

旧版配置驱动:

步骤1: 设置驱动(setup)

WebDriverManager.chromedriver().setup();

步骤2: 创建驱动实例

WebDriver driver = new ChromeDriver();

3. frame处理

frame介绍:

1)frame 是 html 中的框架。可以在同一个浏览器中显示多个页面。

2)frame 标签包含 frameset、frame、iframe 三种。

frameset和frame需要配套使用,frame嵌套在frameset中,frameset中可以有多个frame;frame是frameset中的单个框架

iframe是独立的,可以直接嵌套在html中,iframe可以多层嵌套,自己套自己;

了解frame原因 :如果不进行 frame 切换,就无法定位到 frame 中的元素进行后续的自动化操作。

【注】现在使用较多的是iframe框架

frameset :和普通的标签一样,不会影响正常的定位。

frame/iframe :可以使用 index、id、name、webelement 任意种方式定位。

为什么需要切换框架?

DOM树隔离

每个 frame/iframe 都有独立的 DOM树 -#document

WebDriver 一次只能操作一个 DOM 树

必须切换到对应框架才能定位其中的元素

frameset - html5已废弃

typescript 复制代码
<!-- frame 必须放在 frameset 内 -->
<frameset rows="50%,50%">
  <frame src="top.html" name="topFrame">
  <frame src="bottom.html" name="bottomFrame">
</frameset>

iframe(内联框架)-主流使用

作用:在当前页面中嵌入另一个HTML文档

typescript 复制代码
<!-- iframe 示例(现代用法) -->
<body>
  <h1>主页面</h1>
  <iframe id="contentFrame" name="myIframe" 
    src="embedded.html" width="800" height="600"
    title="内嵌内容"
  ></iframe>
  <p>其他内容...</p>
</body>

常用方法:切换frame框架的方法

java 复制代码
//3种方式切入frame框架中
webDriver.switchTo().frame(id)	切换到 frame
webDriver.switchTo().frame(name)	切换到 frame
webDriver.switchTo().frame(WebElement)	切换到 frame
//切换到父框架
webDriver.switchTo().parentFrame()	
//切回到最外层
webDriver.switchTo().defaultContent()	

单frame处理小练习:

java 复制代码
    /**
     * frame框架的处理
     *  frame框架相当于一个单独的DOM树,需要切进frame才能操作其内元素
     *  原理:driver只能操作一个DOM树
     *  断言小技巧:如使用assertAll();点击assertAll(),进入其中选择对应方法参数,复制出来用
     *   不容易导包错误;
     */
    @Test
    public  void frame() {
        List<Executable> executableList = new ArrayList<>();
        //1.获取驱动
        WebDriver driver = WebDriverManager.chromedriver().create();
        //2.隐式等待
        webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        //3.打开操作的界面
        String url = "https://study_up_up/frame";
        webDriver.get(url);
        //4.业务逻辑操作-获取第一个frame框架中按钮的文本
        System.out.println("---------开始---------");
        //5.frame框架切换
        //5.1 切入第一个框架
        webDriver.switchTo().frame("frame1");
        WebElement frame1_element = webDriver.findElement(By.xpath("//*[@id='frame_btn']"));
        String frame1_elementText = frame1_element.getText();
        System.out.println("第一个frame按钮的文本:" + frame1_elementText);
        executableList.add(() -> {
            assertThat(frame1_elementText, containsString("ui_frame1"));
        });
        //5.2 切入第二个frame: 先切入父框架,再切入第二个frame
        webDriver.switchTo().parentFrame();
        webDriver.switchTo().frame("Main");
        WebElement frame2_element = webDriver.findElement(By.xpath("//*[@class='el-button el-button--success']"));
        String frame2_elementText = frame2_element.getText();
        System.out.println("第二个frame按钮的文本:" + frame2_elementText);
        executableList.add(() -> {
            assertThat(frame2_elementText, equalTo("练习按钮ui_frame3"));
        });
        //整体断言使用
        assertAll(executableList);
    }

多frame框架处理代码演示:

java 复制代码
@Test
    public void moreFrame() throws InterruptedException {
        List<Executable> executableList = new ArrayList<>();
        //1.声明webdriver
        WebDriver driver = WebDriverManager.chromedriver().create();
        //2.声明隐式等待
        webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        //3.打开操作界面
        String url = "https://study_up_up/frame";
        webDriver.get(url);
        //4.业务逻辑操作-获取第二层frame中的文本
        //4.1 iframe两层切换,先切换到第一层,再切换到第二层
        WebElement fir_frame = webDriver.findElement(By.xpath("//*[@class='iframe-div']/iframe"));
        //使用元素定位,切入第一层
        webDriver.switchTo().frame(fir_frame);
        WebElement sec_frame = webDriver.findElement(By.xpath("//*[@class='ma-2 frame2']/iframe"));
        //使用元素定位,切入第二层
        webDriver.switchTo().frame(sec_frame);
        //获取第二层frame框架中的文本
        WebElement element = webDriver.findElement(By.xpath("//*[@id='main-message']/h1/span/span"));
        String text = element.getText();
        System.out.println("第二层frame中的文本:" + text);
        executableList.add(() -> {
            assertThat(text, equalTo("cn.vuejs.org"));
        });
        //5.切换到外层,定位其他元素,defaultContent()-切回到最外层
        webDriver.switchTo().defaultContent();
        WebElement element2 = webDriver.findElement(By.xpath("//*[@id='locate_id']"));
        String text2 = element2.getText();
        System.out.println("text2: " + text2);
        //5.断言
        assertAll(executableList);
    }

【注】

第一次运行: 下载驱动,后续运行: 使用缓存(除非版本变化)

WebDriver driver = WebDriverManager.chromedriver().create();

缓存位置通常:

Windows: C:\Users<user>.cache\selenium

macOS/Linux: ~/.cache/selenium

使用场景推荐:

✅ 新项目开发

✅ 个人学习和测试

✅ 快速原型

✅ 中小型项目


总结

✨✨✨各位读友,本篇分享到内容是否更好的让你理解了 (),如果对你有帮助给个👍赞鼓励一下吧!!
🎉🎉🎉世上没有绝望的处境,只有对处境绝望的人。
🎉🎉🎉一遇挫折就灰心丧气的人,永远是个失败者。而一向努力奋斗,坚韧不拔的人会走向成功。
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!

相关推荐
kylezhao201922 分钟前
C# 语言基础(变量、数据类型、流程控制、面向对象编程)
开发语言·计算机视觉·c#·visionpro
霖鸣25 分钟前
Minecraft通过kubejs进行简单魔改
javascript
咯哦哦哦哦29 分钟前
WSL + ubantu22.04 + 远程桌面闪退+黑屏闪退解决
linux·开发语言
JackieDYH37 分钟前
HTML+CSS+JavaScript实现图像对比滑块demo
javascript·css·html
翩若惊鸿_43 分钟前
【无标题】
开发语言·c#
Da Da 泓1 小时前
多线程(七)【线程池】
java·开发语言·线程池·多线程
杰瑞不懂代码1 小时前
基于 MATLAB 的 BPSK/QPSK/2DPSK 在 AWGN 信道下的 BER 性能仿真与对比分析
开发语言·matlab·qpsk·2psk·2dpsk
小鸡脚来咯2 小时前
python虚拟环境
开发语言·python
全栈前端老曹2 小时前
【前端路由】Vue Router 嵌套路由 - 配置父子级路由、命名视图、动态路径匹配
前端·javascript·vue.js·node.js·ecmascript·vue-router·前端路由
龘龍龙2 小时前
Python基础(九)
android·开发语言·python