Selenium WebDriver使用经验杂记

原文地址:http://blog.csdn.net/ant_yan/article/details/8185899

(一) To Handle Ajax

Selenium WebDriver在加载页面的时候,无论是driver.get(url)或者driver.getPageSource(),会立即返回当时页面的数据。但当代的网页技术都大量使用了异步Ajax调用,这使得许多DOM元素的创建和加载,都分布在页面load结束后的许多零散的时间点,让WebDriver的findElement经常无功而返。这个时候,一般有两种方式来解决问题:


1. 利用Selenium WebDriver原生的API,进行预判等待元素出现
演示代码如下:
使用FluentWait + Predicate接口
[java] view plain copy print ?
  1. FluentWait<By> fluentWait = new FluentWait<By>(By.tagName("TEXTAREA"));   
  2. fluentWait.pollingEvery(100, TimeUnit.MILLISECONDS);   
  3. fluentWait.withTimeout(1000, TimeUnit.MILLISECONDS);   
  4. fluentWait.until(new Predicate<By>() {   
  5.     public boolean apply(By by) {   
  6.         try {   
  7.             return browser.findElement(by).isDisplayed();   
  8.         } catch (NoSuchElementException ex) {   
  9.             return false;   
  10.         }   
  11.     }   
  12. });   
  13.  
  14. browser.findElement(By.tagName("TEXTAREA")).sendKeys("text to enter");  

或者也可以直接使用Wait接口的until方法,传入一个ExpectedCondition
[java] view plain copy print ?
  1. public ExpectedCondition<WebElement> visibilityOfElementLocated(final By by) {   
  2.     return new ExpectedCondition<WebElement>() {   
  3.           public WebElement apply(WebDriver driver) {   
  4.             WebElement element = driver.findElement(by);   
  5.             return element.isDisplayed() ? element :null;   
  6.           }   
  7.     };   
  8. }   
  9.      
  10. public void performSomeAction() {   
  11.     ..   
  12.     ..   
  13.     Wait<WebDriver> wait = new WebDriverWait(driver,20);   
  14.     WebElement element = wait.until(visibilityOfElementLocated(By.tagName("a")));   
  15.     ..           
  16. }   

2. 利用Javascript注入,来读取不同Ajax调用框架的Ajax request status,一直等到Ajax调用全部返回才开始分析操作Dom元素
演示代码如下:

[java] view plain copy print ?
  1. protected void syncAjaxByJQuery(String timeout) {   
  2.     boolean isSucceed = false;   
  3.     try {   
  4.         selenium.waitForCondition(   
  5.                 "selenium.browserbot.getCurrentWindow().jQuery.active == 0", timeout);   
  6.         isSucceed = true;   
  7.     } catch (SeleniumException se) {   
  8.         LOG.error(se);   
  9.     } catch (Exception re) {   
  10.         throw new RuntimeException(re.getMessage());   
  11.     }   
  12.     operationCheck(isSucceed);   
  13.  
  14.  
  15. protected void syncAjaxByPrototype(String timeout) {   
  16.     boolean isSucceed = false;   
  17.     try {   
  18.         selenium.waitForCondition(   
  19.                 "selenium.browserbot.getCurrentWindow().Ajax.activeRequestCount == 0",   
  20.                 timeout);   
  21.         isSucceed = true;   
  22.     } catch (SeleniumException se) {   
  23.         LOG.error(se);   
  24.     } catch (Exception re) {   
  25.         throw new RuntimeException(re.getMessage());   
  26.     }   
  27.     operationCheck(isSucceed);   
  28.  
  29.  
  30. protected void syncAjaxByDojo(String timeout) {   
  31.     boolean isSucceed = false;   
  32.     try {   
  33.         selenium.waitForCondition(   
  34.                 "selenium.browserbot.getCurrentWindow().dojo.io.XMLHTTPTransport.inFlight.length == 0",   
  35.                 timeout);   
  36.         isSucceed = true;   
  37.     } catch (SeleniumException se) {   
  38.         LOG.error(se);   
  39.     } catch (Exception re) {   
  40.         throw new RuntimeException(re.getMessage());   
  41.     }   
  42.     operationCheck(isSucceed);   


(二) To Handle Alert

最近遇到许多网页用Selenium操作的时候,网页本身会弹出许多Alert模态窗口,这种模态窗口会阻断driver继续执行。虽然WebDriver已经提供了些许方法来处理模态Alert窗口,但是依然会在不同的浏览器上存在未修复的Bug。先来看看WebDriver提供的处理Alert的方式,基本就是通过driver.switchTo()方法获得Alert模态窗口的句柄,然后调用Alert.accept()或者Alert.dismiss()方法接受或者拒绝,同样的操作也适用于Confirm窗口。我为了防止意外情况下WebDriver内置Alert处理的代码会崩溃,所以特意设计了一个超时机制,一旦超时就转而使用原始键盘模拟输入回车来消除窗口,使用的也是java.awt.Robot类来模拟操作系统的键盘输入动作。


演示代码如下:     
[java] view plain copy print ?
  1. public T handleAlert(){ 
  2.     KeyboardUtils.enter(); 
  3.     long clockTime = System.currentTimeMillis(); 
  4.     long timeout = 3 *1000
  5.     while(true){ 
  6.         if((System.currentTimeMillis()-clockTime) > timeout){ 
  7.             KeyboardUtils.enter(); 
  8.             break
  9.         }else if(WebDriverUtils.isAlertExist(driver)){ 
  10.             try
  11.                 Alert alert = driver.switchTo().alert(); 
  12.                 alert.getText(); 
  13.                 alert.accept(); 
  14.             }catch(Exception ex){ 
  15.                 KeyboardUtils.enter(); 
  16.             } 
  17.         } 
  18.          
  19.         waiting(300); 
  20.     }    
  21.     return instance; 
  22.  
  23.  
  24. //In WebDriverUtils.class 
  25. public staticboolean isAlertExist(WebDriver driver){ 
  26.     try
  27.         driver.switchTo().alert(); 
  28.         return true
  29.     }catch(NoAlertPresentException  ex){ 
  30.         return false
  31.     } 


(三) To Handle more than Alert
还有一种情况,当我用Firefox打开某些游戏网页的时候,网页本身可能是Flash展示,它会自动触发Firefox弹出收藏窗口,添加本页进浏览器的Bookmark,这种窗口一旦出现,driver直接停止响应,代码本身都不会再继续往下走了。所以更别提你下面还有处理Alert窗口的代码,那都是浮云,本线程直接就阻塞了,这一度让我非常的火大。后来实在没办法,想了一种折中的方式,设计了另外一个线程进行心跳检测。主线程如果一直都持续运行,定期汇报心跳。一旦超过一段时间没有收到心跳信号,那么独立的心跳检测线程就会试图去模拟回车输入,或者其他动作,来进一步确保浏览器不会被各种奇怪的现象阻塞不能继续。


(四) Other Tips
1. 加载IEDriver的时候,通常会因为兼容模式的设置问题,而无法启动,尝试在创建IEDriver对象的时候,加入合适的参数设置:

[java] view plain copy print ?
  1. DesiredCapabilities ieCapabilities = DesiredCapabilities.internetExplorer();  
  2. ieCapabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,true); 
  3. return new InternetExplorerDriver(ieCapabilities); 

2. 关闭浏览器的时候,例如新浪主页,本身会弹出一个独立的广告窗口,如果调用driver.close的时候句柄焦点在主窗口上,那么广告窗口是无法关闭的。所以在关闭driver之前,试图检查一下还有无别的独立窗口句柄存在:
[java] view plain copy print ?
  1. public staticvoid closeWebDriver(WebDriver driver){ 
  2.     if(driver == null
  3.         return
  4.      
  5.     try
  6.         String current = driver.getWindowHandle(); 
  7.         Set<String> otherWins = driver.getWindowHandles(); 
  8.         for(String winId : otherWins) 
  9.             if(winId.equals(current)) 
  10.                 continue
  11.             else 
  12.                 driver.switchTo().window(winId).close(); 
  13.     }catch(Exception ex){ 
  14.         //ignore 
  15.     }finally
  16.         try
  17.             driver.quit(); 
  18.         }catch(Exception ex){} 
  19.     } 

3. 如何使用WebDriver进行网页截图,提示一点在Flash页面中截图可能会得到一片黑色的屏幕,另外WebDriver截图会把当时整个网页哪怕是需要滚动才显示出来的部分,都会包含在截图里面。
[java] view plain copy print ?
  1. public static String takeScreenshot(WebDriver driver, String savePath,long delay){ 
  2.      try
  3.         CommonUtils.waiting(delay * 1000); 
  4.         File screenShotFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); 
  5.         String path = "screenshot/"+savePath; 
  6.         if(!savePath.endsWith("/.png")) 
  7.             path = path + ".png"
  8.          
  9.         FileUtils.copyFile(screenShotFile, new File(path)); 
  10.         LOG.info("Take screenshot at "+savePath); 
  11.         return path; 
  12.     } catch (IOException ex) { 
  13.         LOG.warn("failed to take screenshot for current page, caused by "+ex.getMessage()); 
  14.         return null
  15.     } 

你可能感兴趣的:(java,java,java,java,selenium,selenium)