Selenium 如何免 iframe 切换

环境搭一半就放弃 · 2018年10月17日 · 最后由 我问问 回复于 2018年11月19日 · 2018 次阅读

iframe,切换来切换去,烦死了!

看到这篇文章就算赚到!里面就有解决整天切换 iframe 的方法。
我们公司已经开发了 3 年的 WEB UI 自动化测试框架,但是 iframe 的切换至今是痛点,遇见 iframe 得一层层切换,早在去年本人就提出有没办法去掉 iframe 切换,脚本直接写各种 xpath 即可。
所以本文的重点(因为有同学不晓得本文重点是什么)是如何在自动化测试脚本中,不再出现 driver.switchTo().frame(i);、driver.switchTo().defaultContent();、driver.switchTo().parentFrame() 这些语句,因为如果要切换框架,写脚本的同学得对着 iframe 一个个去数或者写 iframe 的 xpath,还得切换做切换回默认框架,父框架等脚本,非常不方便!如果在日常工作中,你不知道 iframe 是什么,也没有碰到 iframe 那就不需要看这篇文章了
首先我们公司的框架是使用 JAVA 的,解决 iframe 切换的关键是能够通过 xpath 获取界面上的所有元素,选择每个界面上的元素。所以我们需要用到一个第三方包 JsoupXpath 以及一系列相关的关联包,需要的同学可以百度下载,或加我的微信 423462664 领取。
有了包,我们还需要理解 iframe 在 selenium 到底是什么样的。我们可以通过 driver.getPageSource();方法获取 selenium 能够看到的网页代码,当我们对着有 iframe 的页面拉取时,我们会发现,iframe 里的东西都是空的!!

当切换了 iframe 之后,在调用 driver.getPageSource();方法,界面的代码就变成了:

我们看到上面的代码 iframe 的标签不见了,取而代之的是整个页面只出现 iframe 底下的代码,除了 #document 标记,请看下图,下图是在 chrome 里看到的 iframe 底下代码。

思考,如何自动切换 iframe

所以我们想利用文档一次性拿下包括 iframe 的源码是不可能的,本人一开始想到的解决方案是通过 driver.getPageSource();拿到所有包括 iframe 底下源码,再找到需要点击的元素,再逐级向上级寻找,生成相对应 xpath,通过传入 xpath 和 driver 对象进行操作,因为不能够知道主 html 下的 iframe 里的代码,也不知道 iframe 下的 iframe 有无更多的 iframe,要解决这里就必须使用递归,去寻找各个 iframe 里是否有匹配的代码,而 JsoupXpath 里提供了所需要的全部接口。下面是代码:


/**
     * 在各个iframe中寻找元素
     * 作者:李星明
     * 2018-10-17
     * @author Xingming.Li
     * @param driver webdriver对象,注意传递过来时,清除至默认框架,意思是调用
     * @param xpath 需要寻找元素的xpath,该方法仅支持使用xpath时的搜索
     * @return 返回
     */
    public static WebElement findIframeElement(WebDriver driver ,String  xpath)
    {
        String pageSource = driver.getPageSource();
        WebElement element = null;
        try{
            element = driver.findElement(By.xpath(xpath));
        }catch(Exception e)
        {
            e.printStackTrace();
        }
        if(null!= element)
        {
            return element;
        }

        boolean ifHasIframe = jsoupUtil.ifHasIframe(pageSource);
        if(ifHasIframe == true)
        {
            int i = 0;
            while(true)
            {
                try{
                driver.switchTo().frame(i);

                element = findIframeElement(driver,xpath);
                if(null!= element)
                {
                    return element;
                }
                //返回父框架寻找下一个
                driver.switchTo().parentFrame();
                }catch(Exception e)
                {
                    e.printStackTrace();
                    break;
                }
                i++;
            }
        }else
        {

        }
        return null;
    }
public static boolean ifHasIframe(String pageSource)
    {
        String xpath="//iframe";
        JXDocument jxDocument = new JXDocument(pageSource);
        List<Object> rs = jxDocument.sel(xpath);
        for (Object o:rs){
            if (o instanceof Element){
//              int index = ((Element) o).siblingIndex();
//              System.out.println(index);
//              Element element =  (Element) o;
                return true;
            }
            System.out.println(o.toString());
        }
        return false;
    }

通过调用 findIframeElement(WebDriver driver ,String xpath),能够有效找出 html 与所有 iframe 底下匹配的 xpath(注意,匹配的 xpath 必须是唯一)

调用 findIframeElement 注意事项

在实际调用中,可能很多同学已经切换了多个 iframe,findIframeElement 这个方法是在出现了异常之后,进行调用的,调用之前之后都需要做一定的处理,下面是我在点击行为时,调用的代码

                                                        //打印异常
                                                        e.printStackTrace();
                            driver.switchTo().defaultContent();
                            element = jsoupUtil.findIframeElement(driver, rePath);
                            if(null!= element)
                            {

                                System.out.println("查找成功");
                                try{
                                element.click();
                                }catch(Exception e2)
                                {

                                }
                                //如果下一个行为不需要切换框架即可点击,则不要先切回到最主要的框架里去。
//                              driver.switchTo().defaultContent();
                            }else
                            {
                                System.out.println("查找失败");
                                if (null != Parameter.getParameter("uploadExption")
                                        && Parameter.getParameter("uploadExption")
                                                .equals("1"))
                                {
                                    new exceptionAction().reportExption(...);
                                }
                                return failed(...);
                            }
                            }

不同 iframe 下相同的 xpath 控件识别

我们在实际的项目使用中很快就遇到了在 chrome 浏览器复制 xpath,但是在搜索中能够匹配两次 xpath 的控件,必须要通过修改 xpath 使得该控件唯一,才能使得脚本能够正常运行。(下面提供一个实际案例)

如红圈所示,提交与发送按钮在 chrome 浏览器复制 xpath 时居然是一样的!

必须修改成唯一方能放至程序执行。譬如改成如下的:
//*[@id="btnSubmit"][@value="提 交"]

最后感悟

我们遇到问题,首先是要想到能够解决,然后再是有能力去解决,但我却还没看到有人能够贡献出来代码真正解决这些问题,希望各位大神多多加油多多共享自己 的代码。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 8 条回复 时间 点赞

这思路,佩服……无话可说

最后的感悟和 iframe 切换有啥关系吗?

没明白重点在哪

arrow 回复

这篇文章对于存在大量 iframe 的操作的同学,非常有用

恒温 回复

😂 当然有关系,我没有提我用了 docker 啊,还是微服务什么的框架,我只讲如何自动切换框架识别到需要识别的元素

iframe 页面有的要滑动才加载的,getPageSource 不行的吧

hello 回复

这位兄弟提的问题相当好!那只能先滑动,再点击了,理论上对框架代码不用什么修改,写脚本的同事要注意!

最后的感悟和 iframe 切换有啥关系吗?

需要 登录 後方可回應,如果你還沒有帳號按這裡 注册