2016-08-05css
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
返回ios
新建的一個java project,項目名爲autotest,建立以下結構web
圖1 框架結構雛形chrome
返回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>
其中:
右擊項目->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 -->
爲了統一編碼和之後爲了不亂碼的出現,咱們整個環境都使用UTF-8編碼模式
Window-> Preferences,在彈出窗口Preferences中general->workspace, text file encoding 選擇other 選項爲UTF-8
名稱 | 描述 | 連接 |
![]() |
這個類的主要做用是啓動和關閉瀏覽器 | 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 |
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); } } } }
在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>
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; } }
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; } }
在pom.xml中添加代碼以下:
<dependency> <groupId>com.codeborne</groupId> <artifactId>phantomjsdriver</artifactId> <version>1.2.1</version> </dependency>
在testng.xml中添加代碼以下:
<parameter name="driverConfgFilePath" value="config/driver.properties" />
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)); } }
# 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
代碼以下:
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; } }
retrycount=0
sourcecodedir=src/com/demo/test/testcases
sourcecodeencoding=UTF-8
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 + "\"/>"); } } }
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); } } }
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; } }
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 + " — " + 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\"> </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); } } } }
在testng.xml中加入如下代碼:
<dependency> <groupId>com.thoughtworks.qdox</groupId> <artifactId>qdox</artifactId> <version>1.12.1</version> <scope>compile</scope> </dependency>
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沒有得到對象,退出操做失敗"); } } }
在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/" />
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"); }
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"); }
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']"); }
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(); } }
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!"); } }
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); } }
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"); } }
在testng.xml中添加代碼以下:
<!-- 定義測試模塊,用test標籤包圍 --> <test name="Login" preserve-order="true"> <packages> <package name="com.demo.test.testcases.login" /> </packages> </test>
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>
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 -->
Window->Preferences,而後再找到testng選項:Output directory,默認路徑是/test-output 也就是項目根目錄下會生成一個test-output的目錄。修改成:/result/test-report
根據須要添加不一樣瀏覽器的driver。
右擊testng.xml -> runas -> TestNG Suite