一:每次運行都須要打開代碼工具,如eclipse或者IDE等。爲了後面的持續集成,直接使用Maven命令去運行自動化測試,須要引入surfire插件。筆者使用的是2.10版本Surefire和6.9.10版本TESTNG。html
下面是項目完整的pom文件:java
<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.claire.leafly</groupId> <artifactId>projectLemon</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>3.4.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.testng/testng --> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.9.10</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> <version>1.4.01</version> </dependency> <!-- https://mvnrepository.com/artifact/org.uncommons/reportng --> <dependency> <groupId>org.uncommons</groupId> <artifactId>reportng</artifactId> <version>1.1.4</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.google.inject/guice --> <dependency> <groupId>com.google.inject</groupId> <artifactId>guice</artifactId> <version>3.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.1</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> </dependencies> <build> <plugins> <!-- 1:解決每次右鍵項目名-maven->update project 時候,項目jdk版本變了,變回1.5版本或者其餘版本 2: 解決使用maven編譯其餘問題:如提示不能在內部類訪問外部非final局部變量 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.10</version> <configuration> <systemPropertyVariables> <org.uncommons.reportng.escape-output>false</org.uncommons.reportng.escape-output>//是否忽略html,解釋見下圖。與以後在reportNg報告上顯示截圖相關。 </systemPropertyVariables> <testFailureIgnore>true</testFailureIgnore>//測試失敗後,是否忽略並繼續測試 <argLine> -Dfile.encoding=UTF-8 </argLine> <suiteXmlFiles> <suiteXmlFile>register.xml</suiteXmlFile>//表明的是要執行的測試套件名稱 </suiteXmlFiles> </configuration> </plugin> </plugins> </build> </project>
該配置與reportng相關,後面有詳細的解釋和源碼。mysql
在pom中設置好surefire插件以後,便可使用maven test來執行設置好的測試套件。面試
注意點(坑):正則表達式
1.讀者使用的各插件或者jar包版本與筆者不一樣,在執行maventest時,若是未能執行測試套件。請嘗試選擇不一樣的suifire插件版本。sql
2.如上配置的<suiteXmlFile>register.xml</suiteXmlFile>---------------------register.xml該文件通常都放置在根目錄下,不然有時會報錯apache
2、在測試完成以後,一般但願獲得一份完善的測試報告。TESTNG的測試報告,太過簡陋,筆者引入reportNG的jar包。api
1.引入reportng依賴mybatis
2.在register.xml中,添加監聽器NewHtmlReporter,用於渲染reportng 生成的測試報告。dom
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="suitName_register" parallel="false"> <test name="testName_register"> <classes> <!-- <class name="com.demo.auto.claire.testcase.register.Register_FailTester_001" /> --> <class name="com.demo.auto.claire.testcase.register.Register_SuccessTester_002" /> </classes> </test> <!-- Test --> <listeners> <!-- 添加監聽,生成測試報告 --> <listener class-name="org.uncommons.reportng.HTMLReporter" />//先添加該監聽器,後面添加自定義監聽器NewHtmlReporter後,該監聽器就能夠刪掉了
<!-- 添加自定義監聽,該監聽繼承了reportNg的 HTMLReporter-->
<!-- <listener class-name="com.demo.auto.claire.util.NewHtmlReporter" />-->
<!--添加自定義監聽器,在測試失敗的時候進行截圖 -->
<listener class-name="com.demo.auto.claire.listener.ListenerFailTestScreenShot">
</listener>
</listeners>
</suite>
<!-- Suite -->
注意(坑):
對項目右鍵--run as --maven test時,若是報Injector類找不到時,須要引入guice依賴。(也能夠升級testng的版本,查找到依賴了guice jar的相應版本便可)
三:此時使用reportng生成了測試報告,比testng的要美觀一些。可是咱們但願能夠對其進行定製。
1.顯示出每一列的名稱
2.顯示錯誤的截圖.效果以下:
1.在測試完成以後,須要對失敗的用例進行截圖。自定義監聽器,在測試失敗的時候,進行截圖。
看一下關於監聽的繼承和實現關係圖
能夠看到,ITestListener接口中定義了方法onTestFailure,自定義監聽類能夠實現改接口,重寫該方法。可是,此時須要重寫改接口全部的方法。即便方法體裏面爲空,代碼也不會美觀。最後筆者選擇將自定義監聽器繼承類TestListenerAdapter,只重寫該類中的onTestFailure方法便可。下面是代碼示例:
1 package com.demo.auto.claire.listener; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.text.SimpleDateFormat; 6 import java.util.Calendar; 7 8 import org.apache.bcel.generic.SIPUSH; 9 import org.testng.ITestResult; 10 import org.testng.Reporter; 11 import org.testng.TestListenerAdapter; 12 13 import com.demo.auto.claire.base.BaseTester; 14 import com.demo.auto.claire.util.ScreenShotUtil; 15 16 public class ListenerFailTestScreenShot extends TestListenerAdapter { 17 18 @Override 19 public void onTestFailure(ITestResult tr) { 20 21 // 獲取到當前的年月日 22 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); 23 Calendar calendar = Calendar.getInstance(); 24 //calendar.getTime()獲得的時間是這樣的:Fri Aug 31 16:16:51 CST 2018,須要格式化成年月日 25 String time = dateFormat.format(calendar.getTime()); 26 //也能夠直接使用 27 //String time2 = dateFormat.format(new java.util.Date() 28 29 //得到幾點周幾 30 //SimpleDateFormat dateFormat2 = new SimpleDateFormat("EEEE"); 31 //String week = dateFormat2.format(new java.util.Date()); 32 33 // D:\myTest\projectLemon\target\surefire-reports\registertest_register_success_002\2018-08-31 34 35 String outPutDirectory=tr.getTestContext().getOutputDirectory(); 36 //獲取到測試的方法名稱 37 String methodName= tr.getName(); 38 39 String path1 = outPutDirectory +File.separator+methodName +File.separator+time; 40 /*System.out.println("##################################"); 41 System.out.println(path1); 42 System.out.println(outPutDirectory); 43 System.out.println(methodName);*/ 44 45 String imgName=null; 46 47 48 try { 49 //當測試不經過的時候,調用截圖的代碼 50 imgName = ScreenShotUtil.screenShot(BaseTester.driver, path1); 51 } catch (IOException e) { 52 53 e.printStackTrace(); 54 } 55 56 //Reporter.log("測試代碼"); 57 //http://localhost:7777/registertest_register_success_002/2018-09-01%E6%98%9F%E6%9C%9F%E5%85%AD/1535807491038.png 58 //String imgHerf2 ="/" +methodName+"/"+ time+"/"+imgName; 59 String suitName = tr.getTestContext().getCurrentXmlTest().getSuite().getName(); 60 String imgHerf = "../" +String.join("/",suitName, methodName,time,imgName); 61 //下面這句代碼是將截圖添加到reportNg的報告中使用的,log的內容會顯示在報告上 62 Reporter.log("<img src='" + imgHerf +"' width='100' height='100'><a href='"+imgHerf+ "' target='_blank'>查看大圖</a></img>"); 63 // System.out.println("******************************"); 64 // System.out.println(imgHerf); 65 66 67 super.onTestFailure(tr); 68 } 69 70 }
該監聽器監聽到測試失敗以後,會調用截圖幫助類中的方法進行截圖。代碼示例:
package com.demo.auto.claire.util; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.commons.io.FileUtils; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; import org.testng.ITestResult; public class ScreenShotUtil { public static void main(String[] args) { WebDriver driver = SeleniumUtil.getDriver(); //driver.get("http://www.baidu.com"); // screenShot(driver); } public static String screenShot(WebDriver driver,String path) throws IOException { File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); //String path = sr.getTestContext().getOutputDirectory(); //FileUtils.copyFileToDirectory(srcFile, new File(path)); //冒號是路徑非法字符,路徑中不能出現冒號,因此時分秒選擇了使用「-」 String fileName = (new SimpleDateFormat("HHmmss")).format(new Date())+".png"; String pathname = path+File.separator+ fileName ; System.out.println("pathname:"+pathname); FileUtils.copyFile(srcFile, new File(pathname)); return fileName; } }
若是是使用maventest執行的測試,則截圖存儲在target目錄下面.(右鍵test-output,找到同級的target目錄-----------surfire-reports ---------------- 套件名稱----------- 測試名稱 --------------年月日 --------時分秒.png)
若是使用的是testng執行的測試,則截圖保存在test-output--------------------surfire-reports ---------------- 套件名稱----------- 測試名稱 --------------年月日 --------時分秒.png
2.下面講一下,如何定製reportng
a.修改其配置文件,增長列名
上圖中的文本是:(可根據喜愛,自行配置)-----修改完成以後,記得託回到jar包中去
log=Log Info
screenshot=Screen Shot
duration=Duration
打開並修改上述文件:
上圖中的文本是:-----修改完成以後,記得託回到jar包中去
#if ($failedTests.size() > 0) <table class="resultsTable"> <tr><th colspan="4" class="header failed">$messages.getString("failedTests")</th></tr> #foreach ($testClass in $failedTests.keySet()) <tr> <td colspan="1" class="group">$testClass.name</td> <td colspan="1" class="group">$messages.getString("duration")</td> <td colspan="1" class="group">$messages.getString("log")</td> <td colspan="1" class="group">$messages.getString("screenshot")</td> </tr> #set ($classResults = $failedTests.get($testClass)) #parse ("org/uncommons/reportng/templates/html/class-results.html.vm") #end </table> #end
上面兩步,完成了給測試失敗的用例,添加3個列名。
接下來修改源碼和模板文件,將截圖添加進入。
在上述的測試失敗進行截圖的監聽器中,有一句代碼是這樣的
reporter.log("XXX")-------這個xxx的內容就會顯示在咱們的Log info列。---------------下一步,就是要將圖片從log info列刪除,在screen shot列添加:
打開該指定文件。
上圖文本爲:
## Show logger output for the test. #set ($output = $utils.getTestOutput($testResult)) #if ($output.size() > 0) <div class="testOutput"> #foreach( $line in $output ) #if ($meta.shouldEscapeOutput()) $utils.escapeHTMLString($tools.removeImage($line))<br /> #else $tools.removeImage($line)<br/> #end #end </div> #end
上圖文本爲:
<td class="screenshot"> ## Show logger output for the test. #set ($output = $utils.getTestOutput($testResult)) #if ($output.size() > 0) <div class="screenshotimage"> #foreach( $line in $output ) #if ($meta.shouldEscapeOutput()) $utils.escapeHTMLString($tools.getImageString($line))<br /> #else $tools.getImageString($line)<br/> #end #end </div> #end </td>
能夠看到,上面試使用tools.getImageString() 、tools.removeImage來獲取和刪除圖片的。這個tools是哪裏來的呢?
咱們看到reportng本身的方法都是使用的 這個util的對象是從哪裏註冊進來的?
能夠看到HTMLreporter繼承了AbstractReporter,AbstractReporter中定義了一個final的常量UTILS----引用指向的是ReportNGUtils對象,該ReportNGUtils定義了各類方法,供模板文件中調用。又經過creatContext的方式註冊進模板文件(VelocityContext)對象中.
那麼想要在模板文件中刪除和加上圖片,有2種方式,
第一:修改ReportNGUtils類,在其中加上刪除和引入圖片的方法。會涉及到修改源碼,修改源碼一般也有2種方式:
1.下載源碼,修改,完成後從新打jar包並引入------------操做起來比較麻煩
2.在本身的項目下,生成一個與reportng一致的包名和類名(org.uncommons.reportng.HTMLReporter.class),在其中修改代碼。----------這樣的話,若是別人引入了你的jar包又引入了reportng的jar包,要考慮下到底使用的誰的。
第二:自定義一個NewHtmlReporter,去繼承HTMLReporter,重寫父類的creatContext方法,將本身的工具類引入。
下面是NewHtmlReporter 的源碼
package com.demo.auto.claire.util; import org.apache.bcel.generic.NEW; import org.apache.velocity.VelocityContext; import org.uncommons.reportng.HTMLReporter; public class NewHtmlReporter extends HTMLReporter{ private static final ReportNgTools TOOLS = new ReportNgTools(); @Override protected VelocityContext createContext() { VelocityContext context = super.createContext(); context.put("tools", TOOLS); return context; } }
下面是本身實現了刪除和增長截圖的工具類ReportNgTools源碼:
package com.demo.auto.claire.util; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ReportNgTools { public String sayHello() { return "love"; } public String removeImage(String log) { //正則表達式 String regex = "<img .*>.*</img>"; //匹配到正則的內容,將其用空字符替換 return log.replaceAll(regex, ""); } public String getImageString(String log) { //正則表達式 String regex = "<img .*>.*</img>"; //將正則表達式編譯爲Pattern對象 Pattern pattern = Pattern.compile(regex); //經過正則表達式對象去匹配log內容 Matcher matcher = pattern.matcher(log); //若是匹配到了,則返回第0組(第0組是包含了全部匹配到的數據) if (matcher.find()) { return matcher.group(0); } //不然返回空 return ""; } }
此時,咱們直接引入本身的監聽便可:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="suitName_register" parallel="false"> <test name="testName_register"> <classes> <!-- <class name="com.demo.auto.claire.testcase.register.Register_FailTester_001" /> --> <class name="com.demo.auto.claire.testcase.register.Register_SuccessTester_002" /> </classes> </test> <!-- Test --> <listeners> <!-- 添加監聽,生成測試報告。這個就能夠刪除了 --> <!-- <listener class-name="org.uncommons.reportng.HTMLReporter" /> --> <!-- 添加自定義監聽,該監聽繼承了reportNg的 HTMLReporter--> <listener class-name="com.demo.auto.claire.util.NewHtmlReporter" /> <!--添加自定義監聽器,在測試失敗的時候進行截圖 --> <listener class-name="com.demo.auto.claire.listener.ListenerFailTestScreenShot"></listener> </listeners> </suite> <!-- Suite -->
綜上就完成了關於reportng定製的部分