关于Selenium监听器不能监听浏览器实际发生的事件的研究

关于Selenium监听器不能监听浏览器实际发生的事件的研究

由于工作需要,因此进行了Selenium监听器方面的研究,发现一个问题:


Selenium的监听器无法监听浏览器实际发生的事件。

由于本人用的是java+Selenium,因此关于其它语言使用Selenium注册监听器后能否监听到浏览器实际发生的事件的问题并不知晓。如果有朋友发现本文有错误,还请指出,作者会及时进行修改;如果有朋友对于这个问题有更好的解决方法,欢迎评论留言。

下面开始研究正文:

1.为什么Selenium不能监听浏览器实际发生的事件
众所周知,在Java中,我们使用这样的方法注册Selenium监听器:

-------------------------------------------------------------------------
//这是IEDriverServer.exe的绝对路径,selenium启动IE浏览器需要的驱动文件
String driverPath = "G:/IEDriverServer.exe";
//在System中配置IE驱动的路径
System.setProperty("webdriver.ie.driver",driverPath);
//先创建InternetExplorerDriver对象,再创建EventFiringWebDriver对象
//EventFiringWebDriver类实现了WebDriver接口,比原始的WebDriver对象增加了监听器相应方法(WebDriver对象就是Selenium中常用的控制浏览器的对象)
//当代码执行以下这句时,就会自动打开一个IE浏览器
EventFiringWebDriver webDriver = new EventFiringWebDriver(new InternetExplorerDriver());
//创建监听器对象WebEventListenerSelenium,这个类是我自定义的,继承了AbstractWebDriverEventListener类,重写了相应的监听方法
WebEventListenerSelenium listener = new WebEventListenerSelenium();
//Selenium的webDriver对象注册监听器
((EventFiringWebDriver)webDriver).register(listener);
-------------------------------------------------------------------------

在AbstractWebDriverEventListener类中,有以下方法:
    
    //在导航页面之前执行的监听器方法
    public void beforeNavigateTo(String url, WebDriver driver) {
    }
    //在导航页面之后执行的监听器方法
    public void afterNavigateTo(String url, WebDriver driver) {
    }
    //在点击事件之前执行的监听器方法
    public void beforeClickOn(WebElement element, WebDriver driver) {
    }
    //在点击事件之后执行的监听器方法
    public void afterClickOn(WebElement element, WebDriver driver) {
    }    
    //在js执行之前执行的监听器方法
    public void beforeScript(String script, WebDriver driver) {
    }
    //在js执行之后执行的监听器方法
    public void afterScript(String script, WebDriver driver) {
    }    
    //以下不一一列举
    ..................
    
我们可以继承AbstractWebDriverEventListener类,重写需要的方法,之后在webDriver中注册,这样就实现了自定义监听器。


但是,这样有一个问题:
在使用导航方法webDriver.get("www.baidu.com");方法时,我们在监听器中重写的方法beforeNavigateTo(){}、afterNavigateTo(){}是可以按照导航前执行、导航后执行实现监听的;可如果我们在浏览器的地址栏中输入一个网址进行跳转时,beforeNavigateTo(){}、afterNavigateTo(){}中的方法并不会执行。
同理,使用((JavascriptExecutor)webDriver).executeScript("console.log('我是js代码')");方法执行js时,beforeScript(){}、afterScript(){}方法可以执行,但是在浏览器打开开发者工具执行js时,监听器方法beforeScript(){}、afterScript(){}并不会执行;
相应的在代码中使用click()方法时,beforeClickOn(){}、afterClickOn(){}可以执行,但是在浏览器中真正点击一个按钮、超链接时,已经注册好的监听器方法beforeClickOn(){}、afterClickOn(){}还是不会执行。

为什么会这样呢?我使用的就是Selenium通过new InternetExplorerDriver()自动打开的浏览器啊?执行代码明明可以让这个浏览器页面跳转、执行js、触发点击事件,以及触发我写好的监听器,为什么我直接在浏览器上操作就不能触发监听器呢?

解决这个问题,我们需要看一下Selenium的源码。
(1)EventFiringWebDriver类中注册监听器的源码:
private final List<WebDriverEventListener> eventListeners = new ArrayList();
public EventFiringWebDriver register(WebDriverEventListener eventListener) {
        this.eventListeners.add(eventListener);
        return this;
    }
首先,这个类中有一个监听器ArrayList集合,当我们调用注册方法时,就是在集合中添加了一个监听器对象。    

(2)EventFiringWebDriver类中导航方法get()的源码:
public void get(String url) {
        this.dispatcher.beforeNavigateTo(url, this.driver);
        this.driver.get(url);
        this.dispatcher.afterNavigateTo(url, this.driver);
    }
    
看到beforeNavigateTo与afterNavigateTo方法了吗?这个就是我们自定义监听器的方法。它们分别在this.driver.get(url);前后,实现导航网页前后的监听。
也就是说,当我们执行webDriver.get(url)方法时,先执行beforeNavigateTo()方法,然后执行原本的get(url)方法,最后执行afterNavigateTo()方法。看起来就像是监听到导航事件后执行相应监听器方法一样。
至于为什么通过this.dispatcher来调用监听器方法,这个和动态代理有关,不是本文的重点,有兴趣的朋友可以自己看看源码分析一下。

看到这里,我们发现,代码中webDriver执行相应方法会触发监听器的问题解决了,而在浏览器中实际进行导航、点击、执行js为什么不会触发监听器的问题还没有解决。


为什么呢?其实很简单,Selenium没有实现这个功能。

作者查看了WebDriver、EventFiringWebDriver等Selenium的源码,并没有发现关于浏览器实际操作的类似监听器方法,也就是说Selenium的监听器就是只能监听代码事件,而不能监听实际浏览器事件。

还有一个方面可以侧面说明Selenium为什么不能监听页面事件,就是Selenium的工作原理。

Selenium原理:
webdriver是按照server–client的经典设计模式设计的: 
server端就是remote server,可以是任意的浏览器:我们的脚本启动浏览器后,该浏览器就是remote server,它的职责就是等待client发送请求并做出相应。
client端简单说来就是我们的测试代码:我们测试代码中的一些行为,比如打开浏览器,转跳到特定的url等操作是以http请求的方式发送给server端(也就是被测浏览器),server接受请求,执行相应操作,并在response中返回执行状态、返回值等信息。

可以看出,Selenium启动的浏览器在server端,我们写的代码在client端,client端通过传递给server端信息来达到控制效果。
client端的监听器可以监听同在client端的代码,但是对于直接操作浏览器(也就是直接操作server端)触发的事件无法监听。

2.研究背景
下面讲讲作者为什么要进行这项研究。
作者之前是使用java的DJNativeSwing内嵌浏览器的,其中也有一个与Selenium的WebDriver类似的对象:JWebBrowser,它是这样注册监听器的:

//创建一个JWebBrowser对象
JWebBrowser webBrowser = new JWebBrowser(JWebBrowser.destroyOnFinalization());
//注册监听器
webBrowser.addWebBrowserListener(new WebBrowserAdapter(){});

这里的WebBrowserAdapter类类似AbstractWebDriverEventListener类,其中也可以重写一些监听方法,当浏览器跳转页面时执行等。

DJNativeSwing结合JFrame,可以创建一个自定义的浏览器窗口,通过JWebBrowser控制其跳转页面、执行js等,在JWebBrowser注册监听器后,无论是代码中执行导航方法、还是手动实际改变浏览器地址栏导航,监听器都可以监听到。

但是当作者使用Selenium创建浏览器、WebDriver注册监听器后,却发现不能监听浏览器实际发生的事件,就很郁闷......

3.解决方法
如果我就是想写监听器监听浏览器实际发生的事件,怎么办呢?
(1)等待Selenium更新。如果Selenium能够将监听器注册到Server端(浏览器端),那么就可以监听浏览器实际发生的事件了。关于这一点,作者还没有找到Selenium中除了EventFiringWebDriver注册监听器外其余的方法,也许已经有其余注册监听器的方法了呢?如果真的存在,还请朋友们为我指点迷津。
(2)使用DJNativeSwing。DJNativeSwing注册的JWebBrowser监听器是可以监听代码中与浏览器实际发生的事件的,本人亲测,绝对可用。虽然其源码有些复杂,跳来跳去没有看懂。

最后,热烈欢迎朋友们对本文进行批评指点,如有不足,还请指出,作者会及时修改;如有其余Selenium监听浏览器实际发生的事件的方法,还请多多指出,让我们共同学习进步。

原文地址:https://www.cnblogs.com/codeToSuccess/p/13906262.html