Selenium Web 自動化 - 項目實戰(一)

Selenium Web 自動化 - 測試框架(一)

2016-08-05css

 

目錄html

1 框架結構雛形
2 把Java項目轉變成Maven項目
3 加入TestNG配置文件
4 Eclipse編碼修改
5 編寫代碼
  5.1 封裝Selenium操做
  5.2 使用log4j進行日誌輸出
  5.3 封裝測試平臺和測試瀏覽器選擇工具類
  5.4 根據key讀取屬性文件裏面的value值
  5.5 arrow插件解析
    5.5.1 ConfigReader.java
    5.5.2 負責監聽測試運行狀態和結果
    5.5.3 負責失敗的用例重跑的監聽器
    5.5.4 負責生成測試報告的監聽器
  5.6 用例的開頭和結尾設計
  5.7 頁面類設計
  5.8 頁面幫助類設計
  5.9 書寫第一個用例
  5.10 完整的pom.xml和testng.xml
6 配置測試報告目錄
7 填加driver
8 執行用例前端

 源代碼java

自動化項目由maven+TestNG+selenium設計而成。linux

  • maven:是一個項目管理工具,主要用於項目構建,依賴管理,項目信息管理。這裏主要用到它的jar包管理
  • TestNG:是一套根據JUnit 和NUnit思想而構建的利用註釋來強化測試功能的一個測試框架,便可以用來作單元測試,也能夠用來作集成測試。

1 框架結構雛形


 返回ios

新建的一個java project,項目名爲autotest,建立以下結構web

圖1 框架結構雛形chrome

  • base:裏面有個基類 (BaseParpare.java),這個類的主要做用是啓動(啓動瀏覽器使用了TetsNG的@BeforeClass)和關閉瀏覽器的做用(關閉瀏覽器使用了TetsNG的@AftereClass)
  • pages:頁面元素類,每個java類,都是一個頁面,存放的都是對應頁面的全部元素定位值。
  • pageshelper:這個包主要存放的是對應頁面的幫助類
  • plugins:主要存放的是arrow插件以及第三方插件。
  • testcases:顧名思義就是存放測試用例的地方
  • utils:這個包主要是封裝了各類工具類,Selenium api封裝類,讀取屬性文件類和選擇瀏覽器類等
  • config目錄:存儲框架類所需的全部屬性文件,好比arrow的配置文件以及自定義的一些屬性文件。
  • res目錄:主要存放的是driver
  • result目錄:存儲測試框架運行測試用例生成的報告(包含log,截圖等)

2 把Java項目轉變成Maven項目


 返回apache

右擊項目->configure->covert to maven project,修改屬性groupId=com.demo,windows

圖2 Creat new POM

生成pom.xml以下所示:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.demo</groupId>
  <artifactId>autotest</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
View Code

其中:

  • Group Id:填寫你組織機構,好比我這裏就寫的com.demo,例如要是百度能夠寫成com.baidu
  • Artifact Id:能夠理解成爲項目打包成jar包的ID 或者說 jar包名字,通常以項目名命名。

3 加入TestNG配置文件 


 返回

右擊項目->TestNG->Convert to TestNG,生成testng.xml以下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
  <test name="Test">
  </test> <!-- Test -->
</suite> <!-- Suite -->
View Code

4 Eclipse編碼修改


 返回

爲了統一編碼和之後爲了不亂碼的出現,咱們整個環境都使用UTF-8編碼模式

Window-> Preferences,在彈出窗口Preferences中general->workspace, text file encoding 選擇other 選項爲UTF-8

5 編寫代碼


 返回

名稱 描述 連接
 這個類的主要做用是啓動和關閉瀏覽器  5.6

 這個包存放的都是對應頁面的全部元素

 5.7
 這個包主要存放的是對應頁面的幫助類  5.8
 主要存放的是arrow插件以及第三方插件  5.5
存放測試用例的地方。在這個包下,還會有不少子包,子包的個數根據你測試的系統的模塊來劃分  5.9
這個類配置了log  5.2
   5.4
 封裝測試平臺和測試瀏覽器選擇工具類  5.3
 這個類封裝了Selenium API  5.1
 Java Application默認帶的library  
 項目依賴的jar  
 項目編譯生成class文件存放目錄,Eclipse會自動控制。  
arrow的配置文件  5.5.1
driver的配置文件  5.4 
 用ie瀏覽器測試所須要的driver  
 存儲測試框架運行測試用例生成的報告(包含log,截圖等)  
 能夠不用管,由maven控制便可  
 maven的配置文件,項目核心配置,用於構建項目、自動下載項目依賴以及後續的和testng、jenkins配合持續集成等  5.10
   5.10

5.1 封裝Selenium操做

SeleniumUtil.java:封裝一下selenium的api (會調用SelectBrowser.java,後面補上這個類),代碼以下:

package com.demo.test.utils;

import java.io.File;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.NoAlertPresentException;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.ITestContext;
import org.testng.ITestResult;


/**
 * @Description 包裝全部selenium的操做以及通用方法,簡化用例中代碼量
 * */
public class SeleniumUtil {
    /** 使用Log4j,第一步就是獲取日誌記錄器,這個記錄器將負責控制日誌信息 */
    public static Logger logger = Logger.getLogger(SeleniumUtil.class.getName());
    public ITestResult it = null;
    public WebDriver driver = null;
    public WebDriver window = null;

    /***
     * 啓動瀏覽器並打開頁面
     * */
    public void launchBrowser(String browserName, ITestContext context,String webUrl,int timeOut) {
        SelectBrowser select = new SelectBrowser();
        driver = select.selectExplorerByName(browserName, context);
        try {
            maxWindow(browserName);
            waitForPageLoading(timeOut);
            get(webUrl);
        } catch (TimeoutException e) {
            logger.warn("注意:頁面沒有徹底加載出來,刷新重試!!");
            refresh();
             JavascriptExecutor js = (JavascriptExecutor)driver;
            String status= (String)js.executeScript("return document.readyState");
        
            
            logger.info("打印狀態:"+status);
        }

    }

    // ------------------------------- 對窗口進行操做  -----------------------------------
    /**
     * 最大化瀏覽器操做
     * */
    public void maxWindow(String browserName) {
        logger.info("最大化瀏覽器:" + browserName);
        driver.manage().window().maximize();
    }

    /**
     * 設定瀏覽器窗口大小: 設置瀏覽器窗口的大小有下面兩個比較常見的用途:<br>
     * 一、在統一的瀏覽器大小下運行用例,能夠比較容易的跟一些基於圖像比對的工具進行結合
     * ,提高測試的靈活性及廣泛適用性。好比能夠跟sikuli結合,使用sikuli操做flash;<br>
     * 二、在不一樣的瀏覽器大小下訪問測試站點,對測試頁面截圖並保存,而後觀察或使用圖像比對工具對被測頁面的前端樣式進行評測。
     * 好比能夠將瀏覽器設置成移動端大小(320x480),而後訪問移動站點,對其樣式進行評估;<br>
     * */
    public void setBrowserSize(int width, int height) {
        driver.manage().window().setSize(new Dimension(width, height));
    }
    
    /**
     * get方法包裝
     * */
    public void get(String url) {
        driver.get(url);
        logger.info("打開測試頁面:[" + url + "]");
    }

    /**
     * close方法包裝
     * */
    public void close() {
        driver.close();
    }

    /**
     * 退出
     * */
    public void quit() {
        driver.quit();
    }
    
    /**
     * 刷新方法包裝
     * */
    public void refresh() {
        driver.navigate().refresh();
        logger.info("頁面刷新成功!");
    }

    /**
     * 後退方法包裝
     * */
    public void back() {
        driver.navigate().back();
    }

    /**
     * 前進方法包裝
     * */
    public void forward() {
        driver.navigate().forward();
    }
    
    /**
     * 得到頁面的標題
     * */
    public String getTitle() {
        return driver.getTitle();
    }
    
    /**
     * 等待alert出現
     * */
    public Alert switchToPromptedAlertAfterWait(long waitMillisecondsForAlert) throws NoAlertPresentException {
        final int ONE_ROUND_WAIT = 200;
        NoAlertPresentException lastException = null;

        long endTime = System.currentTimeMillis() + waitMillisecondsForAlert;

        for (long i = 0; i < waitMillisecondsForAlert; i += ONE_ROUND_WAIT) {

            try {
                Alert alert = driver.switchTo().alert();
                return alert;
            } catch (NoAlertPresentException e) {
                lastException = e;
            }
            pause(ONE_ROUND_WAIT);

            if (System.currentTimeMillis() > endTime) {
                break;
            }
        }
        throw lastException;
    }
    
    /**
     * @Description 對於windows GUI彈出框,要求輸入用戶名和密碼時,
     *              seleniumm不能直接操做,須要藉助http://modifyusername:modifypassword@yoururl 這種方法
     * 
     * */
    public void loginOnWinGUI(String username, String password, String url) {
        driver.get(username + ":" + password + "@" + url);
    }

    //============================== 對窗口進行操做   ==================================
    
    //------------------------------ 查找元素 -------------------------------------
    /**
     * 包裝查找元素的方法 element
     * */
    public WebElement findElementBy(By by) {
        return driver.findElement(by);
    }

    /**
     * 包裝查找元素的方法 elements
     * */
    public List<WebElement> findElementsBy(By by) {
        return driver.findElements(by);
    }
    
    /**
     * 這是一堆相同的elements中 選擇 其中方的 一個 而後在這個選定的中 繼續定位
     * */
    public WebElement getOneElement(By bys, By by, int index) {
        return findElementsBy(bys).get(index).findElement(by);
    }

    //============================= 查找元素 =========================================
    
    //--------------------------- 判斷元素狀態 ----------------------------------------
    /**
     * 判斷文本是否是和需求要求的文本一致
     * **/
    public void isTextCorrect(String actual, String expected) {
        try {
            Assert.assertEquals(actual, expected);
        } catch (AssertionError e) {
            logger.error("指望的文字是 [" + expected + "] 可是找到了 [" + actual + "]");
            Assert.fail("指望的文字是 [" + expected + "] 可是找到了 [" + actual + "]");

        }
        logger.info("找到了指望的文字: [" + expected + "]");
    }

    /**
     * 判斷編輯框是否是可編輯
     * */
    public void isInputEdit(WebElement element) {
        
    }

    /** 檢查元素是否可用 */
    public boolean isEnabled(WebElement element) {
        boolean isEnable = false;
        if (element.isEnabled()) {
            logger.info("The element: [" + getLocatorByElement(element, ">") + "] is enabled");
            isEnable = true;
        } else if (element.isDisplayed() == false) {
            logger.warn("The element: [" + getLocatorByElement(element, ">") + "] is not enable");
            isEnable = false;
        }
        return isEnable;
    }
    

    /** 檢查元素是否顯示 */
    public boolean isDisplayed(WebElement element) {
        boolean isDisplay = false;
        if (element.isDisplayed()) {
            logger.info("The element: [" + getLocatorByElement(element, ">") + "] is displayed");
            isDisplay = true;
        } else if (element.isDisplayed() == false) {
            logger.warn("The element: [" + getLocatorByElement(element, ">") + "] is not displayed");

            isDisplay = false;
        }
        return isDisplay;
    }
    
    /**檢查元素是否是存在*/
    public  boolean isElementExist(By byElement){
        try{
        findElementBy(byElement);
        return true;
        }catch(NoSuchElementException nee){            
            return false;
        }        
    }
    
    /** 檢查元素是否被勾選 */
    public boolean isSelected(WebElement element) {
        boolean flag = false;
        if (element.isSelected() == true) {
            logger.info("The element: [" + getLocatorByElement(element, ">") + "] is selected");
            flag = true;
        } else if (element.isSelected() == false) {
            logger.info("The element: [" + getLocatorByElement(element, ">") + "] is not selected");
            flag = false;
        }
        return flag;
    }

    /**
     * 判斷實際文本時候包含指望文本
     * 
     * @param actual
     *            實際文本
     * @param expect
     *            指望文本
     */
    public void isContains(String actual, String expect) {
        try {
            Assert.assertTrue(actual.contains(expect));
        } catch (AssertionError e) {
            logger.error("The [" + actual + "] is not contains [" + expect + "]");
            Assert.fail("The [" + actual + "] is not contains [" + expect + "]");
        }
        logger.info("The [" + actual + "] is contains [" + expect + "]");
    }
    
    /** 檢查checkbox是否是勾選 */
    public boolean isCheckboxSelected(By elementLocator) {
        if (findElementBy(elementLocator).isSelected() == true) {
            logger.info("CheckBox: " + getLocatorByElement(findElementBy(elementLocator), ">") + " 被勾選");
            return true;
        } else
            logger.info("CheckBox: " + getLocatorByElement(findElementBy(elementLocator), ">") + " 沒有被勾選");
        return false;

    }

    /**
     * 在給定的時間內去查找元素,若是沒找到則超時,拋出異常
     * */
    public void waitForElementToLoad(int timeOut, final By By) {
        logger.info("開始查找元素[" + By + "]");
        try {
            (new WebDriverWait(driver, timeOut)).until(new ExpectedCondition<Boolean>() {

                public Boolean apply(WebDriver driver) {
                    WebElement element = driver.findElement(By);
                    return element.isDisplayed();
                }
            });
        } catch (TimeoutException e) {
            logger.error("超時!! " + timeOut + " 秒以後還沒找到元素 [" + By + "]");
            Assert.fail("超時!! " + timeOut + " 秒以後還沒找到元素 [" + By + "]");

        }
        logger.info("找到了元素 [" + By + "]");
    }

    /**
     * pageLoadTimeout。頁面加載時的超時時間。由於webdriver會等頁面加載完畢在進行後面的操做,
     * 因此若是頁面在這個超時時間內沒有加載完成,那麼webdriver就會拋出異常
     */
    public void waitForPageLoading(int pageLoadTime) {
        driver.manage().timeouts().pageLoadTimeout(pageLoadTime, TimeUnit.SECONDS);
    }
    //========================== 判斷元素狀態  =======================================
    
    //-------------------------- 對元素操做 ----------------------------------------
    /**
     * 得到元素的文本
     * */
    public String getText(By elementLocator) {
        return driver.findElement(elementLocator).getText().trim();
    }
        
    /**
     * 得到當前select選擇的值
     * */
    public List<WebElement> getCurrentSelectValue(By by){
        List<WebElement> options = null;
        Select s = new Select(driver.findElement(by));
            options =  s.getAllSelectedOptions();
            return options;
    }
    
    /**
     * 得到輸入框的值 這個方法 是針對某些input輸入框 沒有value屬性,可是又想取得input的 值得方法
     * */
    public String getInputValue(String chose,String choseValue) {
        String value = null;
        switch(chose.toLowerCase()){
        case "name":
             String jsName = "return document.getElementsByName('"+choseValue+"')[0].value;"; //把JS執行的值 返回出去
             value = (String)((JavascriptExecutor) driver).executeScript(jsName);
             break;
            
        case "id":
             String jsId = "return document.getElementById('"+choseValue+"').value;"; //把JS執行的值 返回出去
             value = (String)((JavascriptExecutor) driver).executeScript(jsId);
             break;
        
            default:
                logger.error("未定義的chose:"+chose);
                Assert.fail("未定義的chose:"+chose);        
        }
        return value;

    }

    /** 得到CSS value */
    public String getCSSValue(WebElement e, String key) {

        return e.getCssValue(key);
    }
    
    /**
     * 得到元素 屬性的文本
     * */
    public String getAttributeText(By elementLocator, String attribute) {
        return driver.findElement(elementLocator).getAttribute(attribute).trim();
    }    

    /** 根據元素來獲取此元素的定位值 */
    public String getLocatorByElement(WebElement element, String expectText) {
        String text = element.toString();
        String expect = null;
        try {
            expect = text.substring(text.indexOf(expectText) + 1, text.length() - 1);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("failed to find the string [" + expectText + "]");
        }
        return expect;
    }
    
    /**
     * 包裝點擊操做
     * */
    public void click(By byElement) {

        try {
            clickTheClickable(byElement, System.currentTimeMillis(), 2500);
        } catch (StaleElementReferenceException e) {
            logger.error("The element you clicked:[" + byElement + "] is no longer exist!");
            Assert.fail("The element you clicked:[" + byElement + "] is no longer exist!");
        } catch (Exception e) {
            logger.error("Failed to click element [" + byElement + "]");
            Assert.fail("Failed to click element [" + byElement + "]",e);
        }
        logger.info("點擊元素 [" + byElement + "]");
    }
    
    /**
     * 包裝清除操做
     * */
    public void clear(By byElement) {
        try {
            findElementBy(byElement).clear();
        } catch (Exception e) {
            logger.error("清除元素 [" + byElement + "] 上的內容失敗!");
        }
        logger.info("清除元素 [" + byElement  + "]上的內容");
    }

    /**
     * 向輸入框輸入內容
     * */
    public void type(By byElement, String key) {
        try {
            findElementBy(byElement).sendKeys(key);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("輸入 [" + key + "] 到 元素[" + byElement + "]失敗");
            Assert.fail("輸入 [" + key + "] 到 元素[" + byElement + "]失敗");
        }
        logger.info("輸入:[" + key + "] 到 [" + byElement + "]");
    }
    
    /**
     * 模擬鍵盤操做的,好比Ctrl+A,Ctrl+C等 參數詳解:<br>
     * 一、WebElement element - 要被操做的元素 <br>
     * 二、Keys key- 鍵盤上的功能鍵 好比ctrl ,alt等 <br>
     * 三、String keyword - 鍵盤上的字母
     * */
    public void pressKeysOnKeyboard(WebElement element, Keys key, String keyword) {

        element.sendKeys(Keys.chord(key, keyword));
    }

    /**
     * 切換frame - 根據String類型(frame名字)
     * */
    public void inFrame(String frameId) {
        driver.switchTo().frame(frameId);
    }

    /**
     * 切換frame - 根據frame在當前頁面中的順序來定位
     * */
    public void inFrame(int frameNum) {
        driver.switchTo().frame(frameNum);
    }

    /**
     * 切換frame - 根據頁面元素定位
     * */
    public void switchFrame(WebElement element) {
        try {
            logger.info("正在跳進frame:[" + getLocatorByElement(element, ">") + "]");
            driver.switchTo().frame(element);
        } catch (Exception e) {
            logger.info("跳進frame: [" + getLocatorByElement(element, ">") + "] 失敗");
            Assert.fail("跳進frame: [" + getLocatorByElement(element, ">") + "] 失敗");
        }
        logger.info("進入frame: [" + getLocatorByElement(element, ">") +"]成功 ");
    }

    /** 跳出frame */
    public void outFrame() {
        driver.switchTo().defaultContent();
    }
    
    /**
     * 選擇下拉選項 -根據value
     * */
    public void selectByValue(By by, String value) {
        Select s = new Select(driver.findElement(by));
        s.selectByValue(value);
    }

    /**
     * 選擇下拉選項 -根據index角標
     * */
    public void selectByIndex(By by, int index) {
        Select s = new Select(driver.findElement(by));
        s.selectByIndex(index);
    }

    /**
     * 選擇下拉選項 -根據文本內容
     * */
    public void selectByText(By by, String text) {
        Select s = new Select(driver.findElement(by));
        s.selectByVisibleText(text);
    }

    /**
     * 執行JavaScript 方法
     * */
    public void executeJS(String js) {
        ((JavascriptExecutor) driver).executeScript(js);
        logger.info("執行JavaScript語句:[" + js + "]");
    }        

    /**
     * 執行JavaScript 方法和對象
     * 用法:seleniumUtil.executeJS("arguments[0].click();", seleniumUtil.findElementBy(MyOrdersPage.MOP_TAB_ORDERCLOSE));
     * */
    public void executeJS(String js, Object... args) {
        ((JavascriptExecutor) driver).executeScript(js, args);
        logger.info("執行JavaScript語句:[" + js + "]");
    }

    /**
     * 包裝selenium模擬鼠標操做 - 鼠標移動到指定元素
     * */
    public void mouseMoveToElement(By by) {
        Actions builder = new Actions(driver);
        Actions mouse = builder.moveToElement(driver.findElement(by));
        mouse.perform();
    }

    /**
     * 包裝selenium模擬鼠標操做 - 鼠標移動到指定元素
     * */
    public void mouseMoveToElement(WebElement element) {
        Actions builder = new Actions(driver);
        Actions mouse = builder.moveToElement(element);
        mouse.perform();
    }
    
    /**
     * 包裝selenium模擬鼠標操做 - 鼠標右擊
     * */
    public void mouseRightClick(By element) {
        Actions builder = new Actions(driver);
        Actions mouse = builder.contextClick(findElementBy(element));
        mouse.perform();
    }

    /**
     * 上傳文件,須要點擊彈出上傳照片的窗口才行
     * 
     * @param brower
     *            使用的瀏覽器名稱
     * @param file
     *            須要上傳的文件及文件名
     */
    public void handleUpload(String browser, File file) {
        String filePath = file.getAbsolutePath();
        String executeFile = "res/script/autoit/Upload.exe";
        String cmd = "\"" + executeFile + "\"" + " " + "\"" + browser + "\"" + " " + "\"" + filePath + "\"";
        try {
            Process p = Runtime.getRuntime().exec(cmd);
            p.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // ===================== 對元素進行操做 ============================
    
    /**
     * 添加cookies,作自動登錄的必要方法
     * */
    public void addCookies(int sleepTime) {
        pause(sleepTime);
        Set<Cookie> cookies = driver.manage().getCookies();
        for (Cookie c : cookies) {
            System.out.println(c.getName() + "->" + c.getValue());
            if (c.getName().equals("logisticSessionid")) {
                Cookie cook = new Cookie(c.getName(), c.getValue());
                driver.manage().addCookie(cook);
                System.out.println(c.getName() + "->" + c.getValue());
                System.out.println("添加成功");
            } else {
                System.out.println("沒有找到logisticSessionid");
            }

        }

    }

    // webdriver中能夠設置不少的超時時間
    /** implicitlyWait。識別對象時的超時時間。過了這個時間若是對象還沒找到的話就會拋出NoSuchElement異常 */
    public void implicitlyWait(int timeOut) {
        driver.manage().timeouts().implicitlyWait(timeOut, TimeUnit.SECONDS);
    }

    /** setScriptTimeout。異步腳本的超時時間。webdriver能夠異步執行腳本,這個是設置異步執行腳本腳本返回結果的超時時間 */
    public void setScriptTimeout(int timeOut) {
        driver.manage().timeouts().setScriptTimeout(timeOut, TimeUnit.SECONDS);
    }    
    
    /** 得到屏幕的分辨率 - 寬 */
    public static double getScreenWidth() {
        return java.awt.Toolkit.getDefaultToolkit().getScreenSize().getWidth();
    }
    

    /**
     * 暫停當前用例的執行,暫停的時間爲:sleepTime
     * */
    public void pause(int sleepTime) {
        if (sleepTime <= 0) {
            return;
        }
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /** 不能點擊時候重試點擊操做 */
    private void clickTheClickable(By byElement, long startTime, int timeOut) throws Exception {
        try {
            findElementBy(byElement).click();
        } catch (Exception e) {
            if (System.currentTimeMillis() - startTime > timeOut) {
                logger.warn(byElement+ " is unclickable");
                throw new Exception(e);
            } else {
                Thread.sleep(500);
                logger.warn(byElement + " is unclickable, try again");
                clickTheClickable(byElement, startTime, timeOut);
            }
        }
    }

}
View Code

pom.xml中添加代碼以下:

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>LATEST</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>LATEST</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-remote-driver</artifactId>
            <version>LATEST</version>
        </dependency>
View Code

5.2 使用log4j進行日誌輸出

LogConfiguration.java:配置了log的輸出等級,以及如何顯示,如何輸出,輸出的日誌保存到哪裏等配置,代碼以下:

package com.demo.test.utils;
import java.util.Properties;
import org.apache.log4j.PropertyConfigurator;
/**
 * @author young
 * @decription 動態生成各個模塊中的每條用例的日誌,運行完成用例以後請到result/log目錄下查看
 * */
public class LogConfiguration {
    
        public static void initLog(String fileName){
            //獲取到模塊名字
            String founctionName = getFunctionName(fileName);
            //聲明日誌文件存儲路徑以及文件名、格式
            final String logFilePath  = "./result/log/"+founctionName+"/"+fileName+".log";  
            Properties prop = new Properties();
            //配置日誌輸出的格式
            prop.setProperty("log4j.rootLogger","info, toConsole, toFile");
            prop.setProperty("log4j.appender.file.encoding","UTF-8" );
            prop.setProperty("log4j.appender.toConsole","org.apache.log4j.ConsoleAppender");
            prop.setProperty("log4j.appender.toConsole.Target","System.out");
            prop.setProperty("log4j.appender.toConsole.layout","org.apache.log4j.PatternLayout ");
            prop.setProperty("log4j.appender.toConsole.layout.ConversionPattern","[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n");        
            prop.setProperty("log4j.appender.toFile", "org.apache.log4j.DailyRollingFileAppender");
            prop.setProperty("log4j.appender.toFile.file", logFilePath);
            prop.setProperty("log4j.appender.toFile.append", "false");
            prop.setProperty("log4j.appender.toFile.Threshold", "info");
            prop.setProperty("log4j.appender.toFile.layout", "org.apache.log4j.PatternLayout");
            prop.setProperty("log4j.appender.toFile.layout.ConversionPattern", "[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n");
            //使配置生效
            PropertyConfigurator.configure(prop);

        }
        
        
        /**取得模塊名字*/
        public static String getFunctionName(String fileName){
            String functionName = null; 
            int firstUndelineIndex = fileName.indexOf("_"); 
            functionName = fileName.substring(0, firstUndelineIndex-4);
            return functionName;
        
    }
    

}
View Code

5.3 封裝測試平臺和測試瀏覽器選擇工具類

SelectBrowser.java:(會調用PropertiesDataProvider.java,後面補上這個類),代碼以下:

package com.demo.test.utils;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.Assert;
import org.testng.ITestContext;

/**
 * @author young
 * @decription 在不一樣的平臺上選擇對應的瀏覽器,系統平臺程序自動判斷是什麼平臺
 * */
public class SelectBrowser {
    static Logger logger = Logger.getLogger(SelectBrowser.class.getName());

    public WebDriver selectExplorerByName(String browser, ITestContext context) {
        Properties props = System.getProperties(); // 得到系統屬性集
        String currentPlatform = props.getProperty("os.name"); // 操做系統名稱
        logger.info("當前操做系統是:[" + currentPlatform + "]");
        logger.info("啓動測試瀏覽器:[" + browser + "]");
        //從testNG的配置文件讀取參數driverConfgFilePath的值
        String driverConfgFilePath = context.getCurrentXmlTest().getParameter("driverConfgFilePath");
        /** 聲明好驅動的路徑 */
        String chromedriver_win = PropertiesDataProvider.getTestData(driverConfgFilePath, "chromedriver_win");
        String chromedriver_linux = PropertiesDataProvider.getTestData(driverConfgFilePath, "chromedriver_linux");
        String chromedriver_mac = PropertiesDataProvider.getTestData(driverConfgFilePath, "chromedriver_mac");
        String ghostdriver_win = PropertiesDataProvider.getTestData(driverConfgFilePath, "ghostdriver_win");
        String iedriver = PropertiesDataProvider.getTestData(driverConfgFilePath, "iedriver");
        if (currentPlatform.toLowerCase().contains("win")) { //若是是windows平臺

            if (browser.equalsIgnoreCase("ie")) {
                System.setProperty("webdriver.ie.driver", iedriver);
                //IE的常規設置,便於執行自動化測試
                DesiredCapabilities ieCapabilities = DesiredCapabilities.internetExplorer();
                ieCapabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
                //返回ie瀏覽器對象
                return new InternetExplorerDriver(ieCapabilities);
            } else if (browser.equalsIgnoreCase("chrome")) {
                System.setProperty("webdriver.chrome.driver", chromedriver_win);
                //返回谷歌瀏覽器對象
                 return new ChromeDriver();
            } else if (browser.equalsIgnoreCase("firefox")) {
                //返回火狐瀏覽器對象
                return new FirefoxDriver();

            } else if(browser.equalsIgnoreCase("ghost")){
                DesiredCapabilities ghostCapabilities = new DesiredCapabilities();
                ghostCapabilities.setJavascriptEnabled(true);                       
                ghostCapabilities.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, ghostdriver_win);
                //返回ghost對象
                return new PhantomJSDriver(ghostCapabilities);
                
            }else {

                logger.error("The [" + browser + "]" + " explorer is not applicable  for  [" + currentPlatform + "] OS");
                Assert.fail("The [" + browser + "]" + " explorer does not apply to  [" + currentPlatform + "] OS");

            }

        } else if (currentPlatform.toLowerCase().contains("linux")) { //若是是linux平臺

            if (browser.equalsIgnoreCase("chrome")) {
                System.setProperty("webdriver.chrome.driver", chromedriver_linux);
                return new ChromeDriver();

            } else if (browser.equalsIgnoreCase("firefox")) {
                return new FirefoxDriver();
            } else {
                logger.error("The [" + browser + "]" + " explorer does not apply to  [" + currentPlatform + "] OS");
                Assert.fail("The [" + browser + "]" + " explorer does not apply to  [" + currentPlatform + "] OS");
            }

        } else if (currentPlatform.toLowerCase().contains("mac")) { //若是是mac平臺
            if (browser.equalsIgnoreCase("chrome")) {
                System.setProperty("webdriver.chrome.driver", chromedriver_mac);
                return new ChromeDriver();
            } else if (browser.equalsIgnoreCase("firefox")) {
                return new FirefoxDriver();
            } else {
                logger.error("The [" + browser + "]" + " explorer does not apply to  [" + currentPlatform + "] OS");
                Assert.fail("The [" + browser + "]" + " explorer does not apply to  [" + currentPlatform + "] OS");
            }

        } else
            logger.error("The [" + currentPlatform + "] is not supported for this automation frame,please change the OS(Windows,MAC or LINUX)");
        
        Assert.fail("The [" + currentPlatform + "] is not supported for this automation frame,please change the OS(Windows,MAC or LINUX)");
        return null;
    }
}
View Code

pom.xml中添加代碼以下:

        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>phantomjsdriver</artifactId>
            <version>1.2.1</version>
        </dependency>
View Code

testng.xml中添加代碼以下:

    <parameter name="driverConfgFilePath" value="config/driver.properties" />

5.4 根據key讀取屬性文件裏面的value值

PropertiesDataProvider.java:從.properties文件中讀取相關測試數據,代碼以下:

package com.demo.test.utils;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;

/**
 * @Desription 從.properties文件中讀取相關測試數據<br>
 * 
 * */
public class PropertiesDataProvider {

    public static String getTestData(String configFilePath, String key) {
        Configuration config = null;
        try {
            config = new PropertiesConfiguration(configFilePath);
        } catch (ConfigurationException e) {
            e.printStackTrace();
        }
        return String.valueOf(config.getProperty(key));

    }
}
View Code

driver.properties代碼以下:

# define the browser driver here 
#chrome driver for windows chromedriver_win =res/driver/chrome/win/chromedriver.exe #chrome driver for linux chomedriver_linux =res/driver/chrome/linux/chromedriver #chrome driver for mac os chomedriver_mac = res/driver/chrome/mac/chromedriver #chrome driver for IE iedriver = res/driver/ie/iedriver.exe #ghost driver for windows ghostdriver_win =res/driver/ghostdriver/phantomjs.exe
View Code

5.5 arrow插件解析

5.5.1 ConfigReader.java

代碼以下:

package com.demo.test.plugins.arrow.utils;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;
import org.testng.log4testng.Logger;

public class ConfigReader {
    private static Logger logger = Logger.getLogger(ConfigReader.class);
    private static ConfigReader cr;
    private int retryCount = 0;
    private String sourceCodeDir = "src";
    private String sourceCodeEncoding = "UTF-8";
    private static final String RETRYCOUNT = "retrycount";
    private static final String SOURCEDIR = "sourcecodedir";
    private static final String SOURCEENCODING = "sourcecodeencoding";
    private static final String CONFIGFILE = "./config/config.properties";

    private ConfigReader() {
        readConfig(CONFIGFILE);
    }

    public static ConfigReader getInstance() {
        if (cr == null) {
            cr = new ConfigReader();
        }
        return cr;
    }

    private void readConfig(String fileName) {
        Properties properties = getConfig(fileName);
        if (properties != null) {
            String sRetryCount = null;

            Enumeration<?> en = properties.propertyNames();
            while (en.hasMoreElements()) {
                String key = (String) en.nextElement();
                if (key.toLowerCase().equals(RETRYCOUNT)) {
                    sRetryCount = properties.getProperty(key);
                }
                if (key.toLowerCase().equals(SOURCEDIR)) {
                    sourceCodeDir = properties.getProperty(key);
                }
                if (key.toLowerCase().equals(SOURCEENCODING)) {
                    sourceCodeEncoding = properties.getProperty(key);
                }
            }
            if (sRetryCount != null) {
                sRetryCount = sRetryCount.trim();
                try {
                    retryCount = Integer.parseInt(sRetryCount);
                } catch (final NumberFormatException e) {
                    throw new NumberFormatException("Parse " + RETRYCOUNT + " [" + sRetryCount + "] from String to Int Exception");
                }
            }
        }
    }

    public int getRetryCount() {
        return this.retryCount;
    }

    public String getSourceCodeDir() {
        return this.sourceCodeDir;
    }

    public String getSrouceCodeEncoding() {
        return this.sourceCodeEncoding;
    }

    /**
     * 
     * @param propertyFileName
     * 
     * @return
     */
    private Properties getConfig(String propertyFileName) {
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream(propertyFileName));
        } catch (FileNotFoundException e) {
            properties = null;
            logger.warn("FileNotFoundException:" + propertyFileName);
        } catch (IOException e) {
            properties = null;
            logger.warn("IOException:" + propertyFileName);
        }
        return properties;
    }
}
View Code

config.properties代碼以下:

retrycount=0
sourcecodedir=src/com/demo/test/testcases
sourcecodeencoding=UTF-8

5.5.2 負責監聽測試運行狀態和結果

TestResultListener.java代碼以下:

package com.demo.test.plugins.arrow;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.TestListenerAdapter;

/**
 * @author netease_arrow 描述:來自網易的截圖插件
 * 
 */
public class TestResultListener extends TestListenerAdapter {

    private static Logger logger = Logger.getLogger(TestResultListener.class.getName());
    protected ITestContext testContext = null; // 這裏也是新加的
    String  browser = null;

    @Override
    public void onStart(ITestContext testContext) { // 這裏也是新加的,用於對context進行統一
        this.testContext = testContext;
        browser = String.valueOf(testContext.getCurrentXmlTest().getParameter("browserName"));
        super.onStart(testContext);
    }

    @Override
    public void onTestFailure(ITestResult tr) {
        super.onTestFailure(tr);
        logger.warn(tr.getName() + " 測試用例執行失敗!");
        WebDriver webDriver = (WebDriver) testContext.getAttribute("SELENIUM_DRIVER"); // 這裏就是取driver
        saveScreenShot(tr, webDriver,browser);
    }

    @Override
    public void onTestSkipped(ITestResult tr) {
        super.onTestSkipped(tr);
        WebDriver webDriver = (WebDriver) testContext.getAttribute("SELENIUM_DRIVER");
        logger.warn(tr.getName() + " 測試用例因爲某些緣由被跳過!");
        saveScreenShot(tr, webDriver,browser);

    }

    @Override
    public void onTestSuccess(ITestResult tr) {
        super.onTestSuccess(tr);
        logger.info(tr.getName() + " 測試用例執行成功!");
    }

    @Override
    public void onTestStart(ITestResult tr) {
        super.onTestStart(tr);
        logger.info(tr.getName() + " 測試用例開始執行!");
    }

    @Override
    public void onFinish(ITestContext testContext) {
        super.onFinish(testContext);

        // List of test results which we will delete later
        ArrayList<ITestResult> testsToBeRemoved = new ArrayList<ITestResult>();
        // collect all id's from passed test
        Set<Integer> passedTestIds = new HashSet<Integer>();
        for (ITestResult passedTest : testContext.getPassedTests().getAllResults()) {
            logger.info("執行成功的用例 = " + passedTest.getName());
            passedTestIds.add(getId(passedTest));
        }

        // Eliminate the repeat methods
        Set<Integer> skipTestIds = new HashSet<Integer>();
        for (ITestResult skipTest : testContext.getSkippedTests().getAllResults()) {
            logger.info("被跳過的用例 = " + skipTest.getName());
            // id = class + method + dataprovider
            int skipTestId = getId(skipTest);

            if (skipTestIds.contains(skipTestId) || passedTestIds.contains(skipTestId)) {
                testsToBeRemoved.add(skipTest);
            } else {
                skipTestIds.add(skipTestId);
            }
        }

        // Eliminate the repeat failed methods
        Set<Integer> failedTestIds = new HashSet<Integer>();
        for (ITestResult failedTest : testContext.getFailedTests().getAllResults()) {
            logger.info("執行失敗的用例 = " + failedTest.getName());
            // id = class + method + dataprovider
            int failedTestId = getId(failedTest);

            // if we saw this test as a failed test before we mark as to be
            // deleted
            // or delete this failed test if there is at least one passed
            // version
            if (failedTestIds.contains(failedTestId) || passedTestIds.contains(failedTestId) || skipTestIds.contains(failedTestId)) {
                testsToBeRemoved.add(failedTest);
            } else {
                failedTestIds.add(failedTestId);
            }
        }

        // finally delete all tests that are marked
        for (Iterator<ITestResult> iterator = testContext.getFailedTests().getAllResults().iterator(); iterator.hasNext();) {
            ITestResult testResult = iterator.next();
            if (testsToBeRemoved.contains(testResult)) {
                logger.info("移除重複失敗的用例 = " + testResult.getName());
                iterator.remove();
            }
        }

    }

    private int getId(ITestResult result) {
        int id = result.getTestClass().getName().hashCode();
        id = id + result.getMethod().getMethodName().hashCode();
        id = id + (result.getParameters() != null ? Arrays.hashCode(result.getParameters()) : 0);
        return id;
    }

    private void saveScreenShot(ITestResult tr, WebDriver driver, String browser) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
        String mDateTime = formatter.format(new Date());
        String fileName = mDateTime + "_" + tr.getName();
        String filePath = "";
        try {
            // 這裏能夠調用不一樣框架的截圖功能
            File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            filePath = "result/screenshot/"+ fileName + ".jpg";
            File destFile = new File(filePath);
            FileUtils.copyFile(screenshot, destFile);
            logger.info("["+fileName + "] 截圖成功,保存在:" + "[ " + filePath + " ]");

        } catch (Exception e) {
            filePath = "["+fileName+"]" + " ,截圖失敗,緣由:" + e.getMessage();
            logger.error(filePath);
        }

        if (!"".equals(filePath)) {
            Reporter.setCurrentTestResult(tr);
            Reporter.log(filePath);
            // 把截圖寫入到Html報告中方便查看
            Reporter.log("<img src=\"../../" + filePath + "\"/>");

        }
    }

}
View Code

5.5.3 負責失敗的用例重跑的監聽器

RetryListener.java: (會調用TestngRetry.java,,後面補上這個類),代碼以下:

package com.demo.test.plugins.arrow;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.IRetryAnalyzer;
import org.testng.annotations.ITestAnnotation;

public class RetryListener implements IAnnotationTransformer {

    @SuppressWarnings("rawtypes")
    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
        IRetryAnalyzer retry = annotation.getRetryAnalyzer();
        if (retry == null) {
            annotation.setRetryAnalyzer(TestngRetry.class);
        }
    }
}
View Code

TestngRetry.java代碼以下:

package com.demo.test.plugins.arrow;
import org.apache.log4j.Logger;
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
import org.testng.Reporter;
import com.demo.test.plugins.arrow.utils.ConfigReader;
import com.demo.test.utils.LogConfiguration;

public class TestngRetry implements IRetryAnalyzer {
    static {
        LogConfiguration.initLog("TestngRetryPage_");
    }
    private static Logger logger = Logger.getLogger(TestngRetry.class);
    private int retryCount = 1;
    private static int maxRetryCount;

    static {
        ConfigReader config = ConfigReader.getInstance();
        maxRetryCount = config.getRetryCount();
        logger.info("RetryCount=" + maxRetryCount);
        logger.info("SourceDir=" + config.getSourceCodeDir());
        logger.info("SourceEncoding=" + config.getSrouceCodeEncoding());
    }

    public boolean retry(ITestResult result) {
        if (retryCount <= maxRetryCount) {
            String message = "Retry for: [" + result.getName() + "] on class [" + result.getTestClass().getName() + "] retry " + retryCount + " times";
            logger.info(message);
            Reporter.setCurrentTestResult(result);
            Reporter.log("RunCount=" + (retryCount + 1));
            retryCount++;
            return true;
        }
        return false;
        
    }

    public static int getMaxRetryCount() {
        return maxRetryCount;
    }

    public int getRetryCount() {
        return retryCount;
    }
}
View Code

5.5.4 負責生成測試報告的監聽器

PowerEmailableReporter.java:代碼以下:

package com.demo.test.plugins.arrow;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.testng.IInvokedMethod;
import org.testng.IReporter;
import org.testng.IResultMap;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestClass;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.collections.Lists;
import org.testng.internal.Utils;
import org.testng.log4testng.Logger;
import org.testng.xml.XmlSuite;

import com.demo.test.plugins.arrow.utils.ConfigReader;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;


/**
 * Reported designed to render self-contained HTML top down view of a testing
 * suite.
 */
public class PowerEmailableReporter implements IReporter {
    private static final Logger L = Logger.getLogger(PowerEmailableReporter.class);

    // ~ Instance fields ------------------------------------------------------

    private PrintWriter m_out;

    private int m_row;

    private Integer m_testIndex;

    private Set<Integer> testIds = new HashSet<Integer>();
    private List<Integer> allRunTestIds = new ArrayList<Integer>();
    private JavaDocBuilder builder = new JavaDocBuilder();

    // ~ Methods --------------------------------------------------------------

    /** Creates summary of the run */
    public void generateReport(List<XmlSuite> xml, List<ISuite> suites, String outdir) {
        try {
            m_out = createWriter(outdir);
        } catch (IOException e) {
            L.error("output file", e);
            return;
        }
        ConfigReader cr = ConfigReader.getInstance();
        builder.setEncoding(cr.getSrouceCodeEncoding());
        builder.addSourceTree(new File(cr.getSourceCodeDir()));
        startHtml(m_out);
        generateSuiteSummaryReport(suites);
        testIds.clear();
        generateMethodSummaryReport(suites);
        testIds.clear();
        generateMethodDetailReport(suites);
        testIds.clear();
        endHtml(m_out);
        m_out.flush();
        m_out.close();
    }

    protected PrintWriter createWriter(String outdir) throws IOException {
        new File(outdir).mkdirs();
        return new PrintWriter(new BufferedWriter(new FileWriter(new File(outdir, "power-emailable-report.html"))));
    }

    /**
     * Creates a table showing the highlights of each test method with links to
     * the method details
     */
    protected void generateMethodSummaryReport(List<ISuite> suites) {
        startResultSummaryTable("methodOverview");
        int testIndex = 1;
        for (ISuite suite : suites) {
            if (suites.size() > 1) {
                titleRow(suite.getName(), 5);
            }
            Map<String, ISuiteResult> r = suite.getResults();
            for (ISuiteResult r2 : r.values()) {
                ITestContext testContext = r2.getTestContext();
                String testName = testContext.getName();
                m_testIndex = testIndex;

                resultSummary(suite, testContext.getSkippedConfigurations(), testName, "skipped", " (configuration methods)");
                resultSummary(suite, testContext.getSkippedTests(), testName, "skipped", "");
                resultSummary(suite, testContext.getFailedConfigurations(), testName, "failed", " (configuration methods)");
                resultSummary(suite, testContext.getFailedTests(), testName, "failed", "");
                resultSummary(suite, testContext.getPassedTests(), testName, "passed", "");

                testIndex++;
            }
        }
        m_out.println("</table>");
    }

    /** Creates a section showing known results for each method */
    protected void generateMethodDetailReport(List<ISuite> suites) {
        for (ISuite suite : suites) {
            Map<String, ISuiteResult> r = suite.getResults();
            for (ISuiteResult r2 : r.values()) {
                ITestContext testContext = r2.getTestContext();
                if (r.values().size() > 0) {
                    m_out.println("<h1>" + testContext.getName() + "</h1>");
                }
                resultDetail(testContext.getFailedConfigurations());
                resultDetail(testContext.getFailedTests());
                resultDetail(testContext.getSkippedConfigurations());
                resultDetail(testContext.getSkippedTests());
                resultDetail(testContext.getPassedTests());
            }
        }
    }

    /**
     * @param tests
     */
    private void resultSummary(ISuite suite, IResultMap tests, String testname, String style, String details) {
        if (tests.getAllResults().size() > 0) {
            StringBuffer buff = new StringBuffer();
            String lastClassName = "";
            int mq = 0;
            int cq = 0;
            Map<String, Integer> methods = new HashMap<String, Integer>();
            Set<String> setMethods = new HashSet<String>();
            for (ITestNGMethod method : getMethodSet(tests, suite)) {
                m_row += 1;

                ITestClass testClass = method.getTestClass();
                String className = testClass.getName();
                if (mq == 0) {
                    String id = (m_testIndex == null ? null : "t" + Integer.toString(m_testIndex));
                    titleRow(testname + " &#8212; " + style + details, 5, id);
                    m_testIndex = null;
                }
                if (!className.equalsIgnoreCase(lastClassName)) {
                    if (mq > 0) {
                        cq += 1;
                        m_out.print("<tr class=\"" + style + (cq % 2 == 0 ? "even" : "odd") + "\">" + "<td");
                        if (mq > 1) {
                            m_out.print(" rowspan=\"" + mq + "\"");
                        }
                        m_out.println(">" + lastClassName + "</td>" + buff);
                    }
                    mq = 0;
                    buff.setLength(0);
                    lastClassName = className;
                }
                Set<ITestResult> resultSet = tests.getResults(method);
                long end = Long.MIN_VALUE;
                long start = Long.MAX_VALUE;
                for (ITestResult testResult : tests.getResults(method)) {
                    if (testResult.getEndMillis() > end) {
                        end = testResult.getEndMillis();
                    }
                    if (testResult.getStartMillis() < start) {
                        start = testResult.getStartMillis();
                    }
                }
                mq += 1;
                if (mq > 1) {
                    buff.append("<tr class=\"" + style + (cq % 2 == 0 ? "odd" : "even") + "\">");
                }
                String description = method.getDescription();
                String testInstanceName = resultSet.toArray(new ITestResult[] {})[0].getTestName();
                // Calculate each test run times, the result shown in the html
                // report.
                ITestResult[] results = resultSet.toArray(new ITestResult[] {});
                String methodName = method.getMethodName();
                if (setMethods.contains(methodName)) {
                    methods.put(methodName, methods.get(methodName) + 1);
                } else {
                    setMethods.add(methodName);
                    methods.put(methodName, 0);
                }
                String parameterString = "";
                int count = 0;

                ITestResult result = null;
                if (results.length > methods.get(methodName)) {
                    result = results[methods.get(methodName)];
                    int testId = getId(result);

                    for (Integer id : allRunTestIds) {
                        if (id.intValue() == testId)
                            count++;
                    }
                    Object[] parameters = result.getParameters();

                    boolean hasParameters = parameters != null && parameters.length > 0;
                    if (hasParameters) {
                        for (Object p : parameters) {
                            parameterString = parameterString + Utils.escapeHtml(p.toString()) + " ";
                        }
                    }
                }

                int methodId = method.getTestClass().getName().hashCode();
                methodId = methodId + method.getMethodName().hashCode();
                if (result != null)
                    methodId = methodId + (result.getParameters() != null ? Arrays.hashCode(result.getParameters()) : 0);

                buff.append("<td><a href=\"#m" + methodId + "\">" + qualifiedName(method) + " " + (description != null && description.length() > 0 ? "(\"" + description + "\")" : "") + "</a>" + (null == testInstanceName ? "" : "<br>(" + testInstanceName + ")") + "</td><td>" + this.getAuthors(className, method) + "</td><td class=\"numi\">" + resultSet.size() + "</td>" + "<td>" + (count == 0 ? "" : count) + "</td>" + "<td>" + parameterString + "</td>" + "<td>" + start + "</td>" + "<td class=\"numi\">" + (end - start) + "</td>" + "</tr>");
            }
            if (mq > 0) {
                cq += 1;
                m_out.print("<tr class=\"" + style + (cq % 2 == 0 ? "even" : "odd") + "\">" + "<td");
                if (mq > 1) {
                    m_out.print(" rowspan=\"" + mq + "\"");
                }
                m_out.println(">" + lastClassName + "</td>" + buff);
            }
        }
    }

    /** Starts and defines columns result summary table */
    private void startResultSummaryTable(String style) {
        tableStart(style, "summary");
        m_out.println("<tr><th>Class</th><th>Method</th><th>Authors</th><th># of<br/>Scenarios</th><th>Running Counts</th>" + "<th>Parameters</th><th>Start</th><th>Time<br/>(ms)</th></tr>");
        m_row = 0;
    }

    private String qualifiedName(ITestNGMethod method) {
        StringBuilder addon = new StringBuilder();
        String[] groups = method.getGroups();
        int length = groups.length;
        if (length > 0 && !"basic".equalsIgnoreCase(groups[0])) {
            addon.append("(");
            for (int i = 0; i < length; i++) {
                if (i > 0) {
                    addon.append(", ");
                }
                addon.append(groups[i]);
            }
            addon.append(")");
        }

        return "<b>" + method.getMethodName() + "</b> " + addon;
    }

    private void resultDetail(IResultMap tests) {
        for (ITestResult result : tests.getAllResults()) {
            ITestNGMethod method = result.getMethod();

            int methodId = getId(result);

            String cname = method.getTestClass().getName();
            m_out.println("<h2 id=\"m" + methodId + "\" name=\"m" + methodId + "\" >" + cname + ":" + method.getMethodName() + "</h2>");
            Set<ITestResult> resultSet = tests.getResults(method);
            generateForResult(result, method, resultSet.size());
            m_out.println("<p class=\"totop\"><a href=\"#summary\">back to summary</a></p>");

        }
    }

    private void generateForResult(ITestResult ans, ITestNGMethod method, int resultSetSize) {
        Object[] parameters = ans.getParameters();
        boolean hasParameters = parameters != null && parameters.length > 0;
        if (hasParameters) {
            tableStart("result", null);
            m_out.print("<tr class=\"param\">");
            for (int x = 1; x <= parameters.length; x++) {
                m_out.print("<th>Parameter #" + x + "</th>");
            }
            m_out.println("</tr>");
            m_out.print("<tr class=\"param stripe\">");
            for (Object p : parameters) {
                m_out.println("<td>" + Utils.escapeHtml(p.toString()) + "</td>");
            }
            m_out.println("</tr>");
        }
        List<String> msgs = Reporter.getOutput(ans);
        boolean hasReporterOutput = msgs.size() > 0;
        Throwable exception = ans.getThrowable();
        boolean hasThrowable = exception != null;
        if (hasReporterOutput || hasThrowable) {
            if (hasParameters) {
                m_out.print("<tr><td");
                if (parameters.length > 1) {
                    m_out.print(" colspan=\"" + parameters.length + "\"");
                }
                m_out.println(">");
            } else {
                m_out.println("<div>");
            }
            if (hasReporterOutput) {
                if (hasThrowable) {
                    m_out.println("<h3>Test Messages</h3>");
                }
                for (String line : msgs) {
                    m_out.println(line + "<br/>");
                }
            }
            if (hasThrowable) {
                boolean wantsMinimalOutput = ans.getStatus() == ITestResult.SUCCESS;
                if (hasReporterOutput) {
                    m_out.println("<h3>" + (wantsMinimalOutput ? "Expected Exception" : "Failure") + "</h3>");
                }
                generateExceptionReport(exception, method);
            }
            if (hasParameters) {
                m_out.println("</td></tr>");
            } else {
                m_out.println("</div>");
            }
        }
        if (hasParameters) {
            m_out.println("</table>");
        }
    }

    protected void generateExceptionReport(Throwable exception, ITestNGMethod method) {
        m_out.print("<div class=\"stacktrace\">");
        m_out.print(Utils.stackTrace(exception, true)[0]);
        m_out.println("</div>");
    }

    /**
     * Since the methods will be sorted chronologically, we want to return the
     * ITestNGMethod from the invoked methods.
     */
    private Collection<ITestNGMethod> getMethodSet(IResultMap tests, ISuite suite) {
        List<IInvokedMethod> r = Lists.newArrayList();
        List<IInvokedMethod> invokedMethods = suite.getAllInvokedMethods();

        // Eliminate the repeat retry methods
        for (IInvokedMethod im : invokedMethods) {
            if (tests.getAllMethods().contains(im.getTestMethod())) {
                int testId = getId(im.getTestResult());
                if (!testIds.contains(testId)) {
                    testIds.add(testId);
                    r.add(im);
                }
            }
        }
        Arrays.sort(r.toArray(new IInvokedMethod[r.size()]), new TestSorter());
        List<ITestNGMethod> result = Lists.newArrayList();

        // Add all the invoked methods
        for (IInvokedMethod m : r) {
            result.add(m.getTestMethod());
        }

        // Add all the methods that weren't invoked (e.g. skipped) that we
        // haven't added yet
        // for (ITestNGMethod m : tests.getAllMethods()) {
        // if (!result.contains(m)) {
        // result.add(m);
        // }
        // }

        for (ITestResult allResult : tests.getAllResults()) {
            int testId = getId(allResult);
            if (!testIds.contains(testId)) {
                result.add(allResult.getMethod());
            }
        }

        return result;
    }

    public void generateSuiteSummaryReport(List<ISuite> suites) {
        tableStart("testOverview", null);
        m_out.print("<tr>");
        tableColumnStart("Test");
        tableColumnStart("Methods<br/>Passed");
        tableColumnStart("Scenarios<br/>Passed");
        tableColumnStart("# skipped");
        tableColumnStart("# failed");
        tableColumnStart("Total<br/>Time");
        tableColumnStart("Included<br/>Groups");
        tableColumnStart("Excluded<br/>Groups");
        m_out.println("</tr>");
        NumberFormat formatter = new DecimalFormat("#,##0.0");
        int qty_tests = 0;
        int qty_pass_m = 0;
        int qty_pass_s = 0;
        int qty_skip = 0;
        int qty_fail = 0;
        long time_start = Long.MAX_VALUE;
        long time_end = Long.MIN_VALUE;
        m_testIndex = 1;
        for (ISuite suite : suites) {
            if (suites.size() > 1) {
                titleRow(suite.getName(), 8);
            }
            Map<String, ISuiteResult> tests = suite.getResults();
            for (ISuiteResult r : tests.values()) {
                qty_tests += 1;
                ITestContext overview = r.getTestContext();
                startSummaryRow(overview.getName());

                getAllTestIds(overview, suite);
                int q = getMethodSet(overview.getPassedTests(), suite).size();
                qty_pass_m += q;
                summaryCell(q, Integer.MAX_VALUE);
                q = overview.getPassedTests().size();
                qty_pass_s += q;
                summaryCell(q, Integer.MAX_VALUE);

                q = getMethodSet(overview.getSkippedTests(), suite).size();
                qty_skip += q;
                summaryCell(q, 0);

                q = getMethodSet(overview.getFailedTests(), suite).size();
                qty_fail += q;
                summaryCell(q, 0);

                time_start = Math.min(overview.getStartDate().getTime(), time_start);
                time_end = Math.max(overview.getEndDate().getTime(), time_end);
                summaryCell(formatter.format((overview.getEndDate().getTime() - overview.getStartDate().getTime()) / 1000.) + " seconds", true);
                summaryCell(overview.getIncludedGroups());
                summaryCell(overview.getExcludedGroups());
                m_out.println("</tr>");
                m_testIndex++;
            }
        }
        if (qty_tests > 1) {
            m_out.println("<tr class=\"total\"><td>Total</td>");
            summaryCell(qty_pass_m, Integer.MAX_VALUE);
            summaryCell(qty_pass_s, Integer.MAX_VALUE);
            summaryCell(qty_skip, 0);
            summaryCell(qty_fail, 0);
            summaryCell(formatter.format((time_end - time_start) / 1000.) + " seconds", true);
            m_out.println("<td colspan=\"2\">&nbsp;</td></tr>");
        }
        m_out.println("</table>");
    }

    private void summaryCell(String[] val) {
        StringBuffer b = new StringBuffer();
        for (String v : val) {
            b.append(v + " ");
        }
        summaryCell(b.toString(), true);
    }

    private void summaryCell(String v, boolean isgood) {
        m_out.print("<td class=\"numi" + (isgood ? "" : "_attn") + "\">" + v + "</td>");
    }

    private void startSummaryRow(String label) {
        m_row += 1;
        m_out.print("<tr" + (m_row % 2 == 0 ? " class=\"stripe\"" : "") + "><td style=\"text-align:left;padding-right:2em\"><a href=\"#t" + m_testIndex + "\">" + label + "</a>" + "</td>");
    }

    private void summaryCell(int v, int maxexpected) {
        summaryCell(String.valueOf(v), v <= maxexpected);
    }

    private void tableStart(String cssclass, String id) {
        m_out.println("<table cellspacing=\"0\" cellpadding=\"0\"" + (cssclass != null ? " class=\"" + cssclass + "\"" : " style=\"padding-bottom:2em\"") + (id != null ? " id=\"" + id + "\"" : "") + ">");
        m_row = 0;
    }

    private void tableColumnStart(String label) {
        m_out.print("<th>" + label + "</th>");
    }

    private void titleRow(String label, int cq) {
        titleRow(label, cq, null);
    }

    private void titleRow(String label, int cq, String id) {
        m_out.print("<tr");
        if (id != null) {
            m_out.print(" id=\"" + id + "\"");
        }
        m_out.println("><th colspan=\"" + cq + "\">" + label + "</th></tr>");
        m_row = 0;
    }

    /** Starts HTML stream */
    protected void startHtml(PrintWriter out) {
        out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">");
        out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
        out.println("<head>");
        out.println("<title>TestNG Report</title>");
        out.println("<style type=\"text/css\">");
        out.println("table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}");
        out.println("td,th {border:1px solid #009;padding:.25em .5em}");
        out.println(".result th {vertical-align:bottom}");
        out.println(".param th {padding-left:1em;padding-right:1em}");
        out.println(".param td {padding-left:.5em;padding-right:2em}");
        out.println(".stripe td,.stripe th {background-color: #E6EBF9}");
        out.println(".numi,.numi_attn {text-align:right}");
        out.println(".total td {font-weight:bold}");
        out.println(".passedodd td {background-color: #0A0}");
        out.println(".passedeven td {background-color: #3F3}");
        out.println(".skippedodd td {background-color: #CCC}");
        out.println(".skippedodd td {background-color: #DDD}");
        out.println(".failedodd td,.numi_attn {background-color: #F33}");
        out.println(".failedeven td,.stripe .numi_attn {background-color: #D00}");
        out.println(".stacktrace {white-space:pre;font-family:monospace}");
        out.println(".totop {font-size:85%;text-align:center;border-bottom:2px solid #000}");
        out.println("</style>");
        out.println("</head>");
        out.println("<body>");
    }

    /** Finishes HTML stream */
    protected void endHtml(PrintWriter out) {
        out.println("</body></html>");
    }

    // ~ Inner Classes --------------------------------------------------------
    /** Arranges methods by classname and method name */
    private class TestSorter implements Comparator<IInvokedMethod> {
        // ~ Methods
        // -------------------------------------------------------------

        /** Arranges methods by classname and method name */
        public int compare(IInvokedMethod o1, IInvokedMethod o2) {
            // System.out.println("Comparing " + o1.getMethodName() + " " +
            // o1.getDate()
            // + " and " + o2.getMethodName() + " " + o2.getDate());
            return (int) (o1.getDate() - o2.getDate());
            // int r = ((T) o1).getTestClass().getName().compareTo(((T)
            // o2).getTestClass().getName());
            // if (r == 0) {
            // r = ((T) o1).getMethodName().compareTo(((T) o2).getMethodName());
            // }
            // return r;
        }
    }

    // ~ JavaDoc-specific Methods
    // --------------------------------------------------------
    /**
     * Get ITestNGMethod author(s) string, or class author(s) if no method
     * author is present. Default return value is "unknown".
     * 
     * @param className
     * @param method
     * @return
     */
    private String getAuthors(String className, ITestNGMethod method) {
        JavaClass cls = builder.getClassByName(className);
        DocletTag[] authors = cls.getTagsByName("author");
        // get class authors as default author name
        String allAuthors = "";
        if (authors.length == 0) {
            allAuthors = "unknown";
        } else {
            for (DocletTag author : authors) {
                allAuthors += author.getValue() + " ";
            }
        }
        // get method author name
        JavaMethod[] mtds = cls.getMethods();
        for (JavaMethod mtd : mtds) {
            if (mtd.getName().equals(method.getMethodName())) {
                authors = mtd.getTagsByName("author");
                if (authors.length != 0) {
                    allAuthors = "";
                    for (DocletTag author : authors) {
                        allAuthors += author.getValue() + " ";
                    }
                }
                break;
            }
        }
        return allAuthors.trim();
    }

    /**
     * Get comment string of Java class.
     * 
     * @param className
     * @return
     */
    @SuppressWarnings("unused")
    private String getClassComment(String className) {
        JavaClass cls = builder.getClassByName(className);
        return cls.getComment();
    }

    /**
     * Get ITestResult id by class + method + parameters hash code.
     * 
     * @param result
     * @return
     */
    private int getId(ITestResult result) {
        int id = result.getTestClass().getName().hashCode();
        id = id + result.getMethod().getMethodName().hashCode();
        id = id + (result.getParameters() != null ? Arrays.hashCode(result.getParameters()) : 0);
        return id;
    }

    /**
     * Get All tests id by class + method + parameters hash code.
     * 
     * @param context
     * @param suite
     */
    private void getAllTestIds(ITestContext context, ISuite suite) {
        IResultMap passTests = context.getPassedTests();
        IResultMap failTests = context.getFailedTests();
        List<IInvokedMethod> invokedMethods = suite.getAllInvokedMethods();
        for (IInvokedMethod im : invokedMethods) {
            if (passTests.getAllMethods().contains(im.getTestMethod()) || failTests.getAllMethods().contains(im.getTestMethod())) {
                int testId = getId(im.getTestResult());
                // m_out.println("ALLtestid=" + testId);
                allRunTestIds.add(testId);
            }
        }
    }
}
View Code

testng.xml中加入如下代碼:

        <dependency>
            <groupId>com.thoughtworks.qdox</groupId>
            <artifactId>qdox</artifactId>
            <version>1.12.1</version>
            <scope>compile</scope>
        </dependency>
View Code

5.6 用例的開頭和結尾設計

BaseParpare.java:代碼以下:

package com.demo.test.base;
/**
 * @Description 測試開始 和 測試結束 的操做
 * 
 * */
import java.io.IOException;
import java.util.Iterator;
import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.ITestContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import com.demo.test.utils.LogConfiguration;
import com.demo.test.utils.SeleniumUtil;
public class BaseParpare {
    //輸出本頁面日誌 初始化
    static Logger logger = Logger.getLogger(BaseParpare.class.getName());
    protected SeleniumUtil seleniumUtil = null;
    // 添加成員變量來獲取beforeClass傳入的context參數
    protected ITestContext testContext = null;
    protected String webUrl="";
    protected int timeOut = 0;
    
    @BeforeClass
    /**啓動瀏覽器並打開測試頁面*/
    public void startTest(ITestContext context) {
        LogConfiguration.initLog(this.getClass().getSimpleName());
        seleniumUtil = new SeleniumUtil();
        // 這裏獲得了context值
        this.testContext = context;
        //從testng.xml文件中獲取瀏覽器的屬性值
        String browserName = context.getCurrentXmlTest().getParameter("browserName");
        timeOut = Integer.valueOf(context.getCurrentXmlTest().getParameter("timeOut"));
        webUrl = context.getCurrentXmlTest().getParameter("testurl");
        

        try {
            //啓動瀏覽器launchBrowser方法能夠本身看看,主要是打開瀏覽器,輸入測試地址,並最大化窗口
            seleniumUtil.launchBrowser(browserName, context,webUrl,timeOut);
        } catch (Exception e) {
            logger.error("瀏覽器不能正常工做,請檢查是否是被手動關閉或者其餘緣由",e);
        }
        //設置一個testng上下文屬性,將driver存起來,以後可使用context隨時取到,主要是提供arrow 獲取driver對象使用的,由於arrow截圖方法須要一個driver對象
        testContext.setAttribute("SELENIUM_DRIVER", seleniumUtil.driver);
    }

    @AfterClass
    /**結束測試關閉瀏覽器*/
    public void endTest() {
        if (seleniumUtil.driver != null) {
            //退出瀏覽器
            seleniumUtil.quit();
        } else {
            logger.error("瀏覽器driver沒有得到對象,退出操做失敗");
            Assert.fail("瀏覽器driver沒有得到對象,退出操做失敗");
        }
    }
}
View Code

在testng.xml添加以下代碼:

    <!-- chrome,firefox,ghost和ie不區分大小寫 -->
    <parameter name="browserName" value="chrome" />
    <!-- 頁面元素10秒不出現超時時間 -->
    <parameter name="timeOut" value="20" />
    <!-- 定義測試的站點 -->
    <parameter name="testurl" value="http://127.0.0.1:1080/WebTours/" />
View Code

5.7 頁面類設計

FramePage.java:代碼以下:

package com.demo.test.pages;

import org.openqa.selenium.By;

/**
 * @description 這個類算不上一個page頁,由於這個WebTours站點涉及到的frame比較多,因此咱們把frame抓取出來用page頁來存儲
 * */
public class FramePage {
    
    /**header frame名字 */
    public static final By FP_FRAME_HEADER = By.name("header");
    /** body frame 名字 */
    public static final By FP_FRAME_BODY = By.name("body");
    /**navbar frame名字*/
    public static final By FP_FRAME_NAVBAR = By.name("navbar");
    /**info frame名字*/
    public static final By FP_FRAME_INFO = By.name("info");
}
View Code

HomePage.java:代碼以下:

package com.demo.test.pages;

import org.openqa.selenium.By;

/**
 * @description 首頁面元素定位聲明
 * */
public class HomePage {
    
    /**用戶名顯示區域*/
    public static final By HP_TEXT_USERNAME= By.xpath("//blockquote/b");
    /**Flights按鈕*/
    public static final By HP_BUTTON_FLIGHTS = By.xpath("//*[@src='/WebTours/images/flights.gif']");
    /**Itinerary按鈕*/
    public static final By HP_BUTTON_ITINERARY = By.xpath("//*[@src='/WebTours/images/itinerary.gif']");
    /**Home按鈕*/
    public static final By HP_BUTTON_HOME = By.xpath("//*[@src='/WebTours/images/in_home.gif']");
    /**Sign Off按鈕*/
    public static final By HP_BUTTON_SIGNOFF = By.xpath("//*[@src='/WebTours/images/signoff.gif']");
    /**首頁完整文本*/
    public static final By HP_TEXT_HOME= By.xpath("//blockquote");    
}
View Code

LoginPage.java:代碼以下:

package com.demo.test.pages;

import org.openqa.selenium.By;
/**
 * @description 登陸頁面元素定位聲明
 * */
public class LoginPage {
    /**用戶名輸入框*/
    public static final By LP_INPUT_USERNAME = By.name("username");
    
    /**密碼輸入框*/
    public static final By LP_INPUT_PASSWORD = By.name("password");
    
    /**登陸按鈕*/
    public static final By LP_BUTTON_LOGIN = By.name("login");
    
    /**登陸錯誤信息*/
    public static final By LP_TEXT_ERROR= By.xpath("//*[@color='red']");
    
}
View Code

5.8 頁面幫助類設計

FramePageHelper.java:代碼以下:

package com.demo.test.pageshelper;

import org.openqa.selenium.By;

import com.demo.test.utils.SeleniumUtil;

/**
 * @description 這個幫助類主要是進行frame的跳進和跳出的操做
 * */
public class FramePageHelper {

    /** 進入frame-根據frame的元素定位進入 */
    public static void jumpInToFrame(SeleniumUtil seleniumUtil, By by) {
        seleniumUtil.switchFrame(seleniumUtil.findElementBy(by));
    }

    /** 回到默認的frame */
    public static void jumpOut(SeleniumUtil seleniumUtil) {
        seleniumUtil.outFrame();
    }
}
View Code

HomePageHelper.java:代碼以下:

package com.demo.test.pageshelper;

import org.apache.log4j.Logger;

import com.demo.test.pages.FramePage;
import com.demo.test.pages.HomePage;
import com.demo.test.utils.SeleniumUtil;

/**
 * @desciption 首頁幫助類:專門提供在這個頁面進行操做的方法封裝
 */
public class HomePageHelper {
    // 提供本類中日誌輸出對象
    public static Logger logger = Logger.getLogger(HomePageHelper.class);

    /**
     * @author Young
     * @description 等待首頁元素加載
     * @param seleniumUtil
     *            selenium api封裝引用對象
     * @param timeOut
     *            等待元素超時的時間
     * */
    public static void waitHomePageLoad(SeleniumUtil seleniumUtil, int timeOut) {
        FramePageHelper.jumpOut(seleniumUtil);
        // 等待body frame顯示出來
        seleniumUtil.waitForElementToLoad(timeOut, FramePage.FP_FRAME_BODY);
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);// 先進入到body
                                                                                // frame中
        // 等待navbar frame顯示出來
        seleniumUtil.waitForElementToLoad(timeOut, FramePage.FP_FRAME_NAVBAR);
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_NAVBAR);// 再進入body
                                                                                // frame的子frame:navbar
                                                                                // frame中
        logger.info("開始等待首頁元素加載");
        seleniumUtil.waitForElementToLoad(timeOut, HomePage.HP_BUTTON_FLIGHTS);
        seleniumUtil
                .waitForElementToLoad(timeOut, HomePage.HP_BUTTON_ITINERARY);
        seleniumUtil.waitForElementToLoad(timeOut, HomePage.HP_BUTTON_HOME);
        seleniumUtil.waitForElementToLoad(timeOut, HomePage.HP_BUTTON_SIGNOFF);
        logger.info("首頁元素加載完畢");
        FramePageHelper.jumpOut(seleniumUtil);

    }

    /**
     * @Description 登陸成功以後驗證用戶名是否是正確的
     * */
    public static void checkUserName(SeleniumUtil seleniumUtil, int timeOut,
            String username) {
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);// 先進入到body
                                                                                // frame中
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_INFO);// 先進入到body
                                                                                // frame中的子frame:info
                                                                                // frame中
        logger.info("開始檢查用戶名是否是:" + username);
        seleniumUtil.isTextCorrect(
                seleniumUtil.getText(HomePage.HP_TEXT_USERNAME), username);
        logger.info("用戶名檢查完畢,用戶名是:" + username);

    }

    /**
     * @description 登陸成功以後驗證首頁的文本內容
     * */
    public static void checkHomeText(SeleniumUtil seleniumUtil) {
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);// 先進入到body
                                                                                // frame中
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_INFO);// 先進入到body
                                                                                // frame中的子frame:info
                                                                                // frame中
        seleniumUtil
                .isTextCorrect(
                        seleniumUtil.getText(HomePage.HP_TEXT_HOME),
                        "Welcome, jojo, to the Web Tours reservation pages."
                                + "\n"
                                + "Using the menu to the left, you can search for new flights to book, or review/edit the flights already booked. Don't forget to sign off when you're done!");

    }

}
View Code

LoginPageHelper.java:代碼以下:

package com.demo.test.pageshelper;

import org.apache.log4j.Logger;

import com.demo.test.pages.FramePage;
import com.demo.test.pages.LoginPage;
import com.demo.test.utils.SeleniumUtil;

/**
 * @description 登陸頁面幫助類:提供在這個頁面上作的操做的方法封裝
 * */
public class LoginPageHelper {
    // 提供本類中日誌輸出對象
    public static Logger logger = Logger.getLogger(LoginPageHelper.class);

    /**
     * @description 等待登陸頁面元素加載
     * @param seleniumUtil
     *            selenium api封裝引用對象
     * @param timeOut
     *            等待元素超時的時間
     * */
    public static void waitLoginPageLoad(SeleniumUtil seleniumUtil, int timeOut) {
        seleniumUtil.pause(1000);
        // 對此處的解釋:這個登陸頁面有兩個大frame,一個header一個body ,
        // 而登陸的用戶名、密碼輸入框以及登陸按鈕都在body frame下的navbar frame下,
        // 因此先要進入body frame中,而後在進入navbar frame中,才能查找到登陸界面的相關元素
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);// 先進入到body
                                                                                // frame中
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_NAVBAR);// 再進入body
                                                                                // frame的子frame:navbar
                                                                                // frame中
        logger.info("開始檢查登陸頁面元素");
        seleniumUtil.waitForElementToLoad(timeOut, LoginPage.LP_INPUT_USERNAME);
        seleniumUtil.waitForElementToLoad(timeOut, LoginPage.LP_INPUT_PASSWORD);
        seleniumUtil.waitForElementToLoad(timeOut, LoginPage.LP_BUTTON_LOGIN);
        logger.info("檢查登陸頁面元素完畢");
    }

    /**
     * @description 登陸操做封裝
     * @param seleniumUtil
     *            selenium api封裝引用對象
     * @param username
     *            用戶名值
     * @param password
     *            用戶密碼值
     * */
    public static void typeLoginInfo(SeleniumUtil seleniumUtil,
            String username, String password) {

        logger.info("開始輸入登陸信息");
        // 清空用戶名輸入框
        seleniumUtil.clear(LoginPage.LP_INPUT_USERNAME);
        // 輸入用戶名到用戶名輸入框
        seleniumUtil.type(LoginPage.LP_INPUT_USERNAME, username);
        // 清空密碼輸入框
        seleniumUtil.clear(LoginPage.LP_INPUT_PASSWORD);
        // 輸入密碼到密碼輸入框
        seleniumUtil.type(LoginPage.LP_INPUT_PASSWORD, password);
        logger.info("輸入登陸信息完畢");
        // 點擊登陸按鈕
        seleniumUtil.click(LoginPage.LP_BUTTON_LOGIN);

    }

    /**
     * @description 驗證登陸錯誤信息
     * @param seleniumUtil
     *            selenium api封裝引用對象
     * @param error
     *            錯誤文本
     * */
    public static void checkLoginErrorInfo(SeleniumUtil seleniumUtil,
            String error) {
        FramePageHelper.jumpOut(seleniumUtil);
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_BODY);
        FramePageHelper.jumpInToFrame(seleniumUtil, FramePage.FP_FRAME_INFO);
        seleniumUtil.isTextCorrect(
                seleniumUtil.getText(LoginPage.LP_TEXT_ERROR), error);
    }
}
View Code

5.9 書寫第一個用例

LoginPage_001_LoginSuccessFunction_Test:第一部分:LoginPage代表你是哪一個模塊的;第二部分:001,分用例編號,這個晚點會講到用例編號的做用;第三部分:LoginSuccessFuntion這個是你用例具體要幹嗎的一個簡稱;第四部分:Test每一個用例以這個做爲結尾後綴。每一個部分之間必須如下劃線」_」做爲分隔符 。代碼以下:

package com.demo.test.testcases.login;
import java.util.Map;
import org.testng.annotations.Test;
import com.demo.test.base.BaseParpare;
import com.demo.test.pageshelper.HomePageHelper;
import com.demo.test.pageshelper.LoginPageHelper;
/**
 * @description 登陸以後驗證用戶名是否是正確的
 * */

public class LoginPage_001_LoginSuccessFunction_Test extends BaseParpare{
  @Test
  public void loginSuccessFunction() {
      //等待登陸頁面加載
      LoginPageHelper.waitLoginPageLoad(seleniumUtil, timeOut);
      // 輸入登陸信息
      LoginPageHelper.typeLoginInfo(seleniumUtil,"jojo", "bean");
      //等待首頁元素顯示出來
      HomePageHelper.waitHomePageLoad(seleniumUtil, timeOut);
      //檢查用戶名是否是指望的"jojo"
      HomePageHelper.checkUserName(seleniumUtil, timeOut, "jojo");
  }
}
View Code

在testng.xml中添加代碼以下:

    <!-- 定義測試模塊,用test標籤包圍 -->    
    <test name="Login" preserve-order="true">
        <packages>
            <package name="com.demo.test.testcases.login" />
        </packages>
    </test> 
View Code

5.10 完整的pom.xml和testng.xml

pom.xml代碼以下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.demo</groupId>
    <artifactId>autotest</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>LATEST</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>LATEST</version>
        </dependency>
        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>phantomjsdriver</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.9</version>
        </dependency>
        <dependency>
            <groupId>com.thoughtworks.qdox</groupId>
            <artifactId>qdox</artifactId>
            <version>1.12.1</version>
            <scope>compile</scope>
        </dependency>
        
                <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-remote-driver</artifactId>
            <version>LATEST</version>
        </dependency>
    </dependencies>

    <build>
        <sourceDirectory>src</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
View Code

testng.xml代碼以下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
    <!-- driver的屬性配置文件保存路徑 -->
    <parameter name="driverConfgFilePath" value="config/driver.properties" />
    <!-- chrome,firefox,ghost和ie不區分大小寫 -->
    <parameter name="browserName" value="chrome" />
    <!-- 頁面元素10秒不出現超時時間 -->
    <parameter name="timeOut" value="20" />
    <!-- 定義測試的站點 -->
    <parameter name="testurl" value="http://127.0.0.1:1080/WebTours/" />

    <!-- 定義測試模塊,用test標籤包圍 -->
    <test name="Login" preserve-order="true">
        <packages>
            <package name="com.demo.test.testcases.login" />
        </packages>
    </test>

    <listeners>
        <!-- arrow插件實現用例失敗重跑以及生成測試報告 -->
        <listener class-name="com.demo.test.plugins.arrow.TestResultListener" />
        <listener class-name="com.demo.test.plugins.arrow.RetryListener" />
        <listener class-name="com.demo.test.plugins.arrow.PowerEmailableReporter" />
    </listeners>
</suite> <!-- Suite -->
View Code


 

6 配置測試報告目錄


 返回

Window->Preferences,而後再找到testng選項:Output directory,默認路徑是/test-output 也就是項目根目錄下會生成一個test-output的目錄。修改成:/result/test-report

7 填加driver


 返回

根據須要添加不一樣瀏覽器的driver。

8 執行用例


 返回

右擊testng.xml -> runas -> TestNG Suite

相關文章
相關標籤/搜索