maven+selenium+java+testng+jenkins自動化測試

最近在公司搭建了一套基於maven+selenium+java+testng+jenkins的自動化測試框架,免得以後重寫記錄下

工程目錄

 

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>Realinsight4.0</groupId>
    <artifactId>Realinsight4.0</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Realinsight4.0</name>

    <dependencies>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.141.59</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-server</artifactId>
            <version>3.141.59</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
    
        </dependency>
        <dependency>
            <groupId>org.uncommons</groupId>
            <artifactId>reportng</artifactId>
            <version>1.1.4</version>
            <exclusions>
                <exclusion>
                    <groupId>org.testng</groupId>
                    <artifactId>testng</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>3.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>com.aventstack</groupId>
            <artifactId>extentreports</artifactId>
            <version>3.1.2</version>
        </dependency>
        <dependency>
            <groupId>com.relevantcodes</groupId>
            <artifactId>extentreports</artifactId>
            <version>2.40.2</version>
        </dependency>
        <dependency>
            <groupId>com.vimalselvam</groupId>
            <artifactId>testng-extentsreport</artifactId>
            <version>1.3.1</version>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.17</version>
                <configuration>
                    <suiteXmlFiles>
                        <suiteXmlFile>testng.xml</suiteXmlFile>
                    </suiteXmlFiles>
                </configuration>
            </plugin>
        <!--    
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <properties>
                        <property>
                            <name>usedefaultlisteners</name>
                            <value>false</value>
                        </property>
                        <property>
                            <name>listener</name>
                            <value>org.uncommons.reportng.HTMLReporter,org.uncommons.reportng.JUnitXMLReporter</value>
                        </property>
                    </properties>
                    <workingDirectory></workingDirectory>
                    
                </configuration>
            </plugin>
-->
        </plugins>
    </build>


</project>
View Code

testng.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Realinsight4.0">
<listeners>    
<listener class-name="utils.RetryListener" />
<listener class-name="utils.TestngListener" />
<listener class-name="utils.ExtentReporterListener" />
</listeners>
  <test name="登陸測試" verbose="10" preserve-order="true">
    <classes>
      <class name="testcase.login.login"/>
      <class name="testcase.login.loginWithParas"/>
      <class name="testcase.login.createAccount"/>
      <class name="testcase.login.lockAccount"/>
      <class name="testcase.login.resetPwd"/>
      <class name="testcase.login.logout"/>
    </classes>
  </test>     
<test name="數據集測試" verbose="10" preserve-order="true">
    <classes>
      <class name="testcase.boardsheet.UploadData"/>
      <class name="testcase.boardsheet.AggRegate"/>
      <class name="testcase.boardsheet.AppendData"/>
      <class name="testcase.boardsheet.CreatBySQL"/>
      <class name="testcase.boardsheet.AppendMerge"/>
      <class name="testcase.boardsheet.DimensionalityReduction"/>
      <class name="testcase.boardsheet.ReplaceData"/>
    </classes>
  </test> 
   <test name="儀表板測試" verbose="10" preserve-order="true">
    <classes>
      <class name="testcase.dashboard.CreateDashBoard"/>
      <class name="testcase.dashboard.CreateChart"/>
      <class name="testcase.dashboard.ChartOperation"/>
      <class name="testcase.dashboard.TextOperation"/>
    </classes>
  </test> 
   <test name="刪除數據" verbose="10" preserve-order="true">
    <classes>
      <class name="testcase.boardsheet.Deleteboardsheet"/>
    </classes>
  </test> 
</suite> <!-- Suite -->
View Code

log4j.properties

log4j.rootCategory=INFO, stdout , R   
 
log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender\u00A0
log4j.appender.Threshold = DEBUG\u00A0
log4j.appender.CONSOLE.Target = System.out\u00A0
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout\u00A0
log4j.appender.CONSOLE.layout.ConversionPattern = [framework] % d - % c -%- 4r [ % t] %- 5p % c % x - % m % n\u00A0

--------------------- 
\u4F5C\u8005\uFF1AJXNUleo 
\u6765\u6E90\uFF1ACSDN 
\u539F\u6587\uFF1Ahttps://blog.csdn.net/m0_37874657/article/details/80536086 
\u7248\u6743\u58F0\u660E\uFF1A\u672C\u6587\u4E3A\u535A\u4E3B\u539F\u521B\u6587\u7AE0\uFF0C\u8F6C\u8F7D\u8BF7\u9644\u4E0A\u535A\u6587\u94FE\u63A5\uFF01  
log4j.appender.stdout=org.apache.log4j.ConsoleAppender   
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout   
log4j.appender.stdout.layout.ConversionPattern=[YG] %p [%t] %C.%M(%L) | %m%n   
    
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender   
log4j.appender.R.File=${project}/WEB-INF/logs/app.log  
log4j.appender.R.layout=org.apache.log4j.PatternLayout   
1log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n   
   
log4j.logger.com.neusoft=DEBUG   
log4j.logger.com.opensymphony.oscache=ERROR   
log4j.logger.net.sf.navigator=ERROR   
log4j.logger.org.apache.commons=ERROR   
log4j.logger.org.apache.struts=WARN   
log4j.logger.org.displaytag=ERROR   
log4j.logger.org.springframework=DEBUG   
log4j.logger.com.ibatis.db=WARN   
log4j.logger.org.apache.velocity=FATAL   
   
log4j.logger.com.canoo.webtest=WARN   
   
log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN   
log4j.logger.org.hibernate=DEBUG   
log4j.logger.org.logicalcobwebs=WARN  

log4j.rootCategory=INFO, stdout , R

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n
 
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File=D:\\logs\\YG.log 
log4j.appender.R.layout=org.apache.log4j.PatternLayout
1log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n

log4j.logger.com.neusoft=DEBUG
log4j.logger.com.opensymphony.oscache=ERROR
log4j.logger.net.sf.navigator=ERROR
log4j.logger.org.apache.commons=ERROR
log4j.logger.org.apache.struts=WARN
log4j.logger.org.displaytag=ERROR
log4j.logger.org.springframework=DEBUG
log4j.logger.com.ibatis.db=WARN
log4j.logger.org.apache.velocity=FATAL

log4j.logger.com.canoo.webtest=WARN

log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
log4j.logger.org.hibernate=DEBUG
log4j.logger.org.logicalcobwebs=WARN
View Code

ExtentReporterListener.java

package utils;

import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.ResourceCDN;
import com.aventstack.extentreports.Status;
import com.aventstack.extentreports.model.TestAttribute;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.ChartLocation;
import com.aventstack.extentreports.reporter.configuration.Theme;
import org.testng.*;
import org.testng.xml.XmlSuite;

import java.io.File;
import java.util.*;

public class ExtentReporterListener implements IReporter {
    //生成的路徑以及文件名
    private static final String OUTPUT_FOLDER = "test-output/";
    private static final String FILE_NAME = "index.html";

    private ExtentReports extent;

    @Override
    public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
        init();
        boolean createSuiteNode = false;
        if(suites.size()>1){
            createSuiteNode=true;
        }
        for (ISuite suite : suites) {
            Map<String, ISuiteResult> result = suite.getResults();
            //如果suite裏面沒有任何用例,直接跳過,不在報告裏生成
            if(result.size()==0){
                continue;
            }
            //統計suite下的成功、失敗、跳過的總用例數
            int suiteFailSize=0;
            int suitePassSize=0;
            int suiteSkipSize=0;
            ExtentTest suiteTest=null;
            //存在多個suite的情況下,在報告中將同一個一個suite的測試結果歸爲一類,創建一級節點。
            if(createSuiteNode){
                suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName());
            }
            boolean createSuiteResultNode = false;
            if(result.size()>1){
                createSuiteResultNode=true;
            }
            for (ISuiteResult r : result.values()) {
                ExtentTest resultNode;
                ITestContext context = r.getTestContext();
                if(createSuiteResultNode){
                    //沒有創建suite的情況下,將在SuiteResult的創建爲一級節點,否則創建爲suite的一個子節點。
                    if( null == suiteTest){
                        resultNode = extent.createTest(r.getTestContext().getName());
                    }else{
                        resultNode = suiteTest.createNode(r.getTestContext().getName());
                    }
                }else{
                    resultNode = suiteTest;
                }
                if(resultNode != null){
                    resultNode.getModel().setName(suite.getName()+" : "+r.getTestContext().getName());
                    if(resultNode.getModel().hasCategory()){
                        resultNode.assignCategory(r.getTestContext().getName());
                    }else{
                        resultNode.assignCategory(suite.getName(),r.getTestContext().getName());
                    }
                    resultNode.getModel().setStartTime(r.getTestContext().getStartDate());
                    resultNode.getModel().setEndTime(r.getTestContext().getEndDate());
                    //統計SuiteResult下的數據
                    int passSize = r.getTestContext().getPassedTests().size();
                    int failSize = r.getTestContext().getFailedTests().size();
                    int skipSize = r.getTestContext().getSkippedTests().size();
                    suitePassSize += passSize;
                    suiteFailSize += failSize;
                    suiteSkipSize += skipSize;
                    if(failSize>0){
                        resultNode.getModel().setStatus(Status.FAIL);
                    }
                    resultNode.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",passSize,failSize,skipSize));
                }
                buildTestNodes(resultNode,context.getFailedTests(), Status.FAIL);
                buildTestNodes(resultNode,context.getSkippedTests(), Status.SKIP);
                buildTestNodes(resultNode,context.getPassedTests(), Status.PASS);
            }
            if(suiteTest!= null){
                suiteTest.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",suitePassSize,suiteFailSize,suiteSkipSize));
                if(suiteFailSize>0){
                    suiteTest.getModel().setStatus(Status.FAIL);
                }
            }

        }
//        for (String s : Reporter.getOutput()) {
//            extent.setTestRunnerOutput(s);
//        }

        extent.flush();
    }

    private void init() {
        //文件夾不存在的話進行創建
        File reportDir= new File(OUTPUT_FOLDER);
        if(!reportDir.exists()&& !reportDir .isDirectory()){
            reportDir.mkdir();
        }
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
        // 設置靜態文件的DNS
        //怎麼樣解決cdn.rawgit.com訪問不了的情況
        htmlReporter.config().setEncoding("gbk");
        htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);

        htmlReporter.config().setDocumentTitle("自動化測試報告");
        htmlReporter.config().setReportName("自動化測試報告");
        htmlReporter.config().setChartVisibilityOnOpen(true);
        htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
        htmlReporter.config().setTheme(Theme.STANDARD);
        htmlReporter.config().setCSS(".node.level-1  ul{ display:none;} .node.level-1.active ul{display:block;}");
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        extent.setReportUsesManualConfiguration(true);
    }

    private void buildTestNodes(ExtentTest extenttest, IResultMap tests, Status status) {
        //存在父節點時,獲取父節點的標籤
        String[] categories=new String[0];
        if(extenttest != null ){
            List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll();
            categories = new String[categoryList.size()];
            for(int index=0;index<categoryList.size();index++){
                categories[index] = categoryList.get(index).getName();
            }
        }

        ExtentTest test;

        if (tests.size() > 0) {
            //調整用例排序,按時間排序
            Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() {
                @Override
                public int compare(ITestResult o1, ITestResult o2) {
                    return o1.getStartMillis()<o2.getStartMillis()?-1:1;
                }
            });
            treeSet.addAll(tests.getAllResults());
            for (ITestResult result : treeSet) {
                Object[] parameters = result.getParameters();
                String name="";
                //如果有參數,則使用參數的toString組合代替報告中的name
                for(Object param:parameters){
                    name+=param.toString();
                }
                if(name.length()>0){
                    if(name.length()>50){
                        name= name.substring(0,49)+"...";
                    }
                }else{
                    name = result.getMethod().getMethodName();
                }
                if(extenttest==null){
                    test = extent.createTest(name);
                }else{
                    //作爲子節點進行創建時,設置同父節點的標籤一致,便於報告檢索。
                    test = extenttest.createNode(name).assignCategory(categories);
                }
                //test.getModel().setDescription(description.toString());
                //test = extent.createTest(result.getMethod().getMethodName());
                for (String group : result.getMethod().getGroups())
                    test.assignCategory(group);

                List<String> outputList = Reporter.getOutput(result);
                for(String output:outputList){
                    //將用例的log輸出報告中
                    test.debug(output);
                }
                if (result.getThrowable() != null) {
                    test.log(status, result.getThrowable());
                }
                else {
                    test.log(status, "Test " + status.toString().toLowerCase() + "ed");
                }

                test.getModel().setStartTime(getTime(result.getStartMillis()));
                test.getModel().setEndTime(getTime(result.getEndMillis()));
            }
        }
    }

    private Date getTime(long millis) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        return calendar.getTime();
    }
}
View Code

OverrideRetry.java

package utils;
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;

public class OverrideRetry implements IRetryAnalyzer {
    private int count = 1;
    private int max_count = 3;

    @Override
    public boolean retry(ITestResult result) {
        System.out.println("執行用例:"+result.getName()+",第"+count+"次失敗");
        if (count < max_count) {
            count++;
            return true;
        }
        return false;
    }
}
View Code

ParasUtils.java

package utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class ParasUtils {
    private String db_url;
    private String db_username;
    private String db_password;
    private String db_tableName;
    private String url;
    private String username;
    private String password;
    private String userData;
    private String downloadDir;
    private String uploadDir;
    private String chromePath;

    public void readParas(){
        try {
            InputStream inStream = new FileInputStream(new File(System.getProperty("user.dir")+"\\datas\\Paras.properties"));
            Properties prop = new Properties();    
            prop.load(inStream);    
            setDb_url(prop.getProperty("db_url"));
            setDb_username(prop.getProperty("db_username"));
            setDb_password(prop.getProperty("db_password"));
            setDb_tableName(prop.getProperty("db_tableName"));
            setUrl(prop.getProperty("url"));
            setUsername(prop.getProperty("username")); 
            setPassword(prop.getProperty("password")); 
            setUserData(prop.getProperty("user-data-dir"));
            setUploadDir(prop.getProperty("uploadDir"));
            setDownloadDir(prop.getProperty("downloadDir"));
            setChromePath(prop.getProperty("chromePath"));
            
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
    }

    public String getUrl() {
        readParas();
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        readParas();
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        readParas();
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUserData() {
        readParas();
        return userData;
    }

    public void setUserData(String userData) {
        this.userData = userData;
    }

    public String getDownloadDir() {
        readParas();
        return downloadDir;
    }

    public void setDownloadDir(String downloadDir) {
        this.downloadDir = downloadDir;
    }

    public String getDb_username() {
        return db_username;
    }

    public void setDb_username(String db_username) {
        this.db_username = db_username;
    }

    public String getDb_url() {
        return db_url;
    }

    public void setDb_url(String db_url) {
        this.db_url = db_url;
    }

    public String getDb_password() {
        return db_password;
    }

    public void setDb_password(String db_password) {
        this.db_password = db_password;
    }

    public String getDb_tableName() {
        return db_tableName;
    }

    public void setDb_tableName(String db_tableName) {
        this.db_tableName = db_tableName;
    }

    public void setUp() {
        // TODO Auto-generated method stub
        
    }

    public String getUploadDir() {
        return uploadDir;
    }

    public void setUploadDir(String uploadDir) {
        this.uploadDir = uploadDir;
    }

    public String getChromePath() {
        return chromePath;
    }

    public void setChromePath(String chromePath) {
        this.chromePath = chromePath;
    }
    
}
View Code

ReadCSV.java

package utils;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public 
      
      
package utils;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class ReadCSV {
     public static Object [][] readCSV(String fileName) 
                throws IOException {
            //讀取CSV文件的方法
            List<Object[]> records = new ArrayList<Object[]>();
            String record;
            BufferedReader file = new BufferedReader(new InputStreamReader(new FileInputStream(fileName),"UTF-8"));
            file.readLine();
            while ((record=file.readLine())!=null){
                String fields[] =  record.split(",");
                records.add(fields);
            }
            file.close();

            Object[][] results = new Object[records.size()][];
            for (int i=0; i<records.size();i++){
                results[i] = records.get(i);
            }
            return results;
        }
     
}
View Code

RetryListener.java

package utils;

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 {

    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {

        IRetryAnalyzer retry = annotation.getRetryAnalyzer();
        if (retry == null) {
            annotation.setRetryAnalyzer(OverrideRetry.class); // 這個類名一定要和上方的對上

        }
    }
}
View Code

TestngListener.java

package utils;

import java.io.File;
import java.io.IOException;
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.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;

import common.basic;

public class TestngListener extends TestListenerAdapter {
    private static Logger logger = Logger.getLogger(TestngListener.class);

    @Override
    public void onTestFailure(ITestResult tr) {
        super.onTestFailure(tr);
        logger.info(tr.getName() + "Failure");
        basic basic=(basic)tr.getInstance();
        takeScreenshot(basic.driver);
    }

    @Override
    public void onTestSkipped(ITestResult tr) {
        super.onTestSkipped(tr);
        logger.info(tr.getName() + "Skipped");
        basic basic=(basic)tr.getInstance();
        takeScreenshot(basic.driver);
    }

    @Override
    public void onTestSuccess(ITestResult tr) {
        super.onTestSuccess(tr);
        logger.info(tr.getName() + "Success");
    }

    @Override
    public void onTestStart(ITestResult tr) {
        super.onTestStart(tr);
相關文章
相關標籤/搜索