自動化測試--實現一套徹底解耦的簡單測試框架(二)

一:每次運行都須要打開代碼工具,如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 }
View Code

該監聽器監聽到測試失敗以後,會調用截圖幫助類中的方法進行截圖。代碼示例:

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;
    }

}
View Code

若是是使用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定製的部分

相關文章
相關標籤/搜索