基於Java+Maven+Testng+RestAssured+Allure+Jenkins搭建一個接口自動化框架


本次介紹的框架使用“聚合數據”的免費API作為實例進行框架介紹,在介紹框架搭建前,可以先注冊一個聚合數據賬號進行實操。

一:聚合數據的注冊:

1、百度搜索“聚合數據”,進入到聚合數據官網注冊個賬號。
2、注冊完賬號后,點擊【API】導航即可選用免費的試用接口

image

 

3、選用想用的API分類,本次框架以【新聞頭條】為例

image

 

image

 

image

至此就完成了接口申請,可以在【數據中心】->我的API中查看接口的信息,后邊的框架介紹也是基於該接口API進行接口測試

 

 

二:測試用例的設計

測試用例的設計巧妙之處:

1、參照了Jmeter的參數化方式,將需要參數化的數據用${}包裹起來,后邊在解析Excel時,對${}包裹起來的數據,用正則表達式等技術手段替換成實際環境變量的數據,從而實現了參數化設計

2、有一個提取表達式列,通過編寫每個接口的JSON提取表達式,后邊在解析Excel時,對接口執行后的響應數據用該表達式提取出來,保存到環境變量,如果某些接口需要前置接口的響應數據,我們就可以從環境變量中獲取該數據出來,從而解決了接口關聯的問題。

3、有一個數據庫斷言列,由於接口通常需要與數據庫操作關聯起來,那么通過數據庫斷言,可以使接口測試結果更加准確穩定。

 

接下來就進行實際框架搭建了。

一:首先在pom.xml中引入RestAssured、Testng、EasyPOI、fastJson依賴

   <!--RestAssured依賴-->
<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <version>4.2.0</version>
    <scope>test</scope>
</dependency>
    <!--Testng依賴-->
<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.0.0</version>
    <scope>test</scope>
</dependency>
    <!--easyPoi依賴,有兩個坐標-->
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-annotation</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-base</artifactId>
    <version>4.2.0</version>
</dependency>
    <!--fastJson依賴-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.70</version>
</dependency>

本次框架主要包:

common包:存放測試用例的基類,用於封裝測試用例的共性操作

config包:存放項目的配置信息,像項目的域名信息、測試用例Excel路徑等會在此處配置

enties包:存放項目的實體類

testcases包:存放測試用例

utils包:存放項目的工具類

二:我們現在先來解決測試用例的讀取操作。

1、在src/test目錄下新建一個resources資源目錄,將測試用例放到該目錄下

2、在entries包下新建一個CaseInfo實體類,實體類屬性編寫我們Excel測試用例所有列信息,每個屬性用@Excel注解標注,該注解主要是EasyPOI可以用來映射對象屬性信息,注意:@Excel里的name信息要與表頭字段完全一致,否則無法映射。然后每個屬性添加getter、setter方法,最后再添加一個toString方法。

package com.lrc.entries;

import cn.afterturn.easypoi.excel.annotation.Excel;

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description
 **/
public class CaseInfo {
    @Excel(name = "序號(caseId)")
    private int caseId;

    @Excel(name = "接口模塊(interface)")
    private String interfaceName;

    @Excel(name = "用例標題(title)")
    private String title;

    @Excel(name = "請求頭(requestHeader)")
    private String requestHeader;

    @Excel(name = "請求方式(method)")
    private String method;

    @Excel(name = "接口地址(url)")
    private String url;

    @Excel(name = "參數輸入(inputParams)")
    private String inputParams;

    @Excel(name = "期望返回結果(expected)")
    private String expected;

    @Excel(name = "數據庫斷言")
    private String dbAssert;

    @Excel(name="提取表達式(extractExper)")
    private String extractExper;

    public int getCaseId() {
        return caseId;
    }

    public void setCaseId(int caseId) {
        this.caseId = caseId;
    }

    public String getInterfaceName() {
        return interfaceName;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getRequestHeader() {
        return requestHeader;
    }

    public void setRequestHeader(String requestHeader) {
        this.requestHeader = requestHeader;
    }

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getUrl() {
        return url;
    }

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

    public String getInputParams() {
        return inputParams;
    }

    public void setInputParams(String inputParams) {
        this.inputParams = inputParams;
    }

    public String getExpected() {
        return expected;
    }

    public void setExpected(String expected) {
        this.expected = expected;
    }

    public String getDbAssert() {
        return dbAssert;
    }

    public void setDbAssert(String dbAssert) {
        this.dbAssert = dbAssert;
    }

    public String getExtractExper() {
        return extractExper;
    }

    public void setExtractExper(String extractExper) {
        this.extractExper = extractExper;
    }

    @Override
    public String toString() {
        return "CaseInfo{" +
                "caseId=" + caseId +
                ", interfaceName='" + interfaceName + '\'' +
                ", title='" + title + '\'' +
                ", requestHeader='" + requestHeader + '\'' +
                ", method='" + method + '\'' +
                ", url='" + url + '\'' +
                ", inputParams='" + inputParams + '\'' +
                ", expected='" + expected + '\'' +
                ", dbAssert='" + dbAssert + '\'' +
                ", extractExper='" + extractExper + '\'' +
                '}';
    }
}

3、在config包下新建Contants類,填寫項目的基本配置信息

package com.lrc.config;

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description 項目常規信息配置類
 **/
public class Contants {
    //項目訪問地址
    public static final String PROJECT_URL="v.juhe.cn";
    //項目BASEURI地址
    public static final String BASE_URL="http://"+PROJECT_URL;
    //測試用例路徑
    public static final String EXCEL_PATH="src\\test\\resources\\api_testcases.xls";
    //賬號數據的Key,此處的key是自己申請聚合數據賬號后得到的key
    public static final String KEY="xxxxx";
}

4、在utils包下新建POI操作類EasyPoiExcelUtil,用於解析Excel測試用例

package com.lrc.utils;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.lrc.config.Contants;
import com.lrc.entries.CaseInfo;

import java.io.File;
import java.util.List;

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description Excel解析工具類
 **/
public class EasyPoiExcelUtil {
    /**
     * 使用EasyPOI讀取Excel數據
     * @return 用例list集合
     * 獲取Excel里的所有行
     */
    public static List<CaseInfo> readExcel(int num){
        //讀取測試用例
        File file=new File(Contants.EXCEL_PATH);
        //讀取和導入Excel的參數配置
        ImportParams params=new ImportParams();
        params.setStartSheetIndex(num);
        //讀取測試用例整合成每條用例對象集合
        List<CaseInfo> cases = ExcelImportUtil.importExcel(file, CaseInfo.class, params);
        return cases;
    }


    /**
     * 使用EasyPOI讀取Excel數據
     * @return 用例list集合
     * 獲取Excel里的指定行
     */
    public static List<CaseInfo> readExcelPart(int num,int startNum,int readRows){
        //讀取測試用例
        File file=new File(Contants.EXCEL_PATH);
        //讀取和導入Excel的參數配置
        ImportParams params=new ImportParams();
        //讀取指定頁的Sheet
        params.setStartSheetIndex(num);
        //指定從第幾行開始讀取
        params.setStartRows(startNum);
        //指定讀取幾行數據
        params.setReadRows(readRows);
        //讀取測試用例整合成每條用例對象集合
        List<CaseInfo> cases = ExcelImportUtil.importExcel(file, CaseInfo.class, params);
        return cases;
    }

    /**
     * 使用EasyPOI讀取Excel數據
     * @return 用例list集合
     * 從指定行開始讀取下面全部用例
     */
    public static List<CaseInfo> readExcelPart(int num,int startNum){
        //讀取測試用例
        File file=new File(Contants.EXCEL_PATH);
        //讀取和導入Excel的參數配置
        ImportParams params=new ImportParams();
        //讀取指定頁的Sheet
        params.setStartSheetIndex(num);
        //指定從第幾行開始讀取
        params.setStartRows(startNum);
        //讀取測試用例整合成每條用例對象集合
        List<CaseInfo> cases = ExcelImportUtil.importExcel(file, CaseInfo.class, params);
        return cases;
    }
}

5、我們在testcases包下新建一個Test01類測試下是否能夠解析Excel

package com.lrc.testcases;

import com.lrc.entries.CaseInfo;
import com.lrc.utils.EasyPoiExcelUtil;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.util.List;

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description
 **/
public class Test01 {

    @Test(dataProvider = "readCases")
    public void test01(CaseInfo caseInfo){
        System.out.println(caseInfo);
    }

    @DataProvider
    public Object[] readCases(){
        List<CaseInfo> listDatas = EasyPoiExcelUtil.readExcel(0);
        return listDatas.toArray();
    }
}

執行Test01結果,成功讀取了當前Excel中編寫的4條用例:

自此,Excel用例的讀取操作已經完美解決了。

 

三:接下來講接口的通用常規操作封裝到common包下的BaseTest類中:

1、首先在config包下新建一個Environment類,作為環境變量的接收傳遞類

package com.lrc.config;

import java.util.HashMap;
import java.util.Map;

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description 主要用於全局管理環境變量,模擬Jmeter變量存儲操作
 **/
public class Environment {
    //聲明並定義一個map(類似於JMeter的環境變量)
    public static Map<String,Object> envMap = new HashMap<String, Object>();
}

2、在BaseTest中編寫測試用例的共性操作方法:

package com.lrc.common;

import com.alibaba.fastjson.JSONObject;
import com.lrc.config.Contants;
import com.lrc.config.Environment;
import com.lrc.entries.CaseInfo;
import com.lrc.utils.JDBCUtils;
import io.restassured.RestAssured;
import io.restassured.config.JsonConfig;
import io.restassured.path.json.config.JsonPathConfig;
import io.restassured.response.Response;
import org.testng.Assert;
import org.testng.annotations.BeforeSuite;


import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description
 **/
public class BaseTest {

    @BeforeSuite
    public void beforeMethod(){
        //把json小數的返回類型配置成BigDecimal類型,通過此配置,可以使得我們在斷言小數類型的時候保持數據類型一致,避免了因數據類型不一致而導致斷言不通過的情況
        RestAssured.config = RestAssured.config().jsonConfig(JsonConfig.jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.BIG_DECIMAL));
        //REST-assured基礎 baseurl設置
        RestAssured.baseURI= Contants.BASE_URL;
    }

    /**
     * 封裝所有請求類型
     * @param caseInfo 測試用例對象
     * @return response響應對象
     */
    public static Response request(CaseInfo caseInfo){
        //讀取測試用例的請求頭
        String requestHeaders=caseInfo.getRequestHeader();
        //將請求頭轉為map類型數據
        Map requestHeadersMap= JSONObject.parseObject(requestHeaders);
        //讀取測試用例的url
        String url=caseInfo.getUrl();
        //讀取測試用例的body輸入參數
        String params=caseInfo.getInputParams();
        //讀取測試用例的請求方式
        String method=caseInfo.getMethod();
        //封裝請求方法
        Response response=null;
        if ("get".equalsIgnoreCase(method)) {
            response = RestAssured.given().log().all().headers(requestHeadersMap).when().get(url).then().log().all().extract().response();
        }
        else if ("post".equalsIgnoreCase(method)) {
            response = RestAssured.given().log().all().headers(requestHeadersMap).body(params).when().post(url).then().log().all().extract().response();
        }
        else if ("put".equalsIgnoreCase(method)) {
            response = RestAssured.given().log().all().headers(requestHeadersMap).body(params).when().post(url).then().log().all().extract().response();
        }
     
        return response;
    }

    /**
     * 響應斷言
     * @param res 實際響應結果
     * @param caseInfo 請求數據(實體類)
     */
    public void assertResponse(Response res,CaseInfo caseInfo){
        String expected = caseInfo.getExpected();
        if(expected != null) {
            //轉成Map
            Map<String, Object> expectedMap = JSONObject.parseObject(expected);
            Set<String> allKeySet = expectedMap.keySet();
            for (String key : allKeySet) {
                //獲取實際響應結果
                Object actualResult = res.jsonPath().get(key);
                //獲取期望結果
                Object expectedResult = expectedMap.get(key);
                Assert.assertEquals(actualResult, expectedResult);
            }
        }
    }

    /**
     * 數據庫斷言統一封裝
     * @param caseInfo 用例數據
     */
    public void assertDB(CaseInfo caseInfo){
        String dbAssertInfo = caseInfo.getDbAssert();
        if(dbAssertInfo != null) {
            Map<String, Object> mapDbAssert = JSONObject.parseObject(dbAssertInfo);
            Set<String> allKeys = mapDbAssert.keySet();
            for (String key : allKeys) {
                //key為對應要執行的sql語句
                Object dbActual = JDBCUtils.querySingleData(key);
                //根據數據庫中讀取實際返回類型做判斷
                //1、Long類型
                if(dbActual instanceof Long){
                    Integer dbExpected = (Integer) mapDbAssert.get(key);
                    Long expected = dbExpected.longValue();
                    Assert.assertEquals(dbActual, expected);
                }else {
                    Object expected = mapDbAssert.get(key);
                    Assert.assertEquals(dbActual, expected);
                }
            }
        }
    }


    /**
     * 通過【提取表達式】將對應響應值保存到環境變量中
     * @param res 響應信息
     * @param caseInfo 實體類對象
     */
    public void extractToEnvironment(Response res, CaseInfo caseInfo){
        String extractStr = caseInfo.getExtractExper();
        if(extractStr != null) {
            //把提取表達式轉成Map
            Map<String, Object> map = JSONObject.parseObject(extractStr);
            Set<String> allKeySets = map.keySet();
            for (String key : allKeySets) {
                //key為變量名,value是為提取的gpath表達式
                Object value = map.get(key);
                Object actualValue = res.jsonPath().get((String) value);
                //將對應的鍵和值保存到環境變量中
                Environment.envMap.put(key, actualValue);
            }
        }
    }


    /**
     * 正則替換功能,比如:
     * 原始字符串 {
     *   key=${key}
     * }
     * 替換為
     * {
     *   key=xxxx(自己賬號生成的key)
     * }
     * xxxx 為環境變量中key變量名對應的變量值
     * @param orgStr 源字符串
     * @return
     */
    public String regexReplace(String orgStr){
        if(orgStr != null) {
            //匹配器
            Pattern pattern = Pattern.compile("\\$\\{(.*?)\\}");
            //匹配對象
            Matcher matcher = pattern.matcher(orgStr);
            String result = orgStr;
            //循環遍歷匹配對象
            while (matcher.find()) {
                //獲取整個匹配正則的字符串 ${key}
                String allFindStr = matcher.group(0);
                //找到${XXX}內部的匹配的字符串 key
                String innerStr = matcher.group(1);
                //找到key:xxxx
                //具體的要替換的值(從環境變量中去找到的)
                Object replaceValue = Environment.envMap.get(innerStr);
                //要替換${key} --> xxxx
                result = result.replace(allFindStr, replaceValue + "");
            }
            return result;
        }else{
            return orgStr;
        }
    }

    /**
     * 整條用例數據的參數化替換,只要在對應的用例數據里面有${}包裹起來的數據,那么就會從環境變量中找,如果找到的話就去替換,否則不會
     * @param caseInfo
     */
    public CaseInfo paramsReplace(CaseInfo caseInfo){
        //1、請求頭
        String requestHeader = caseInfo.getRequestHeader();
        caseInfo.setRequestHeader(regexReplace(requestHeader));
        //2、接口地址
        String url = caseInfo.getUrl();
        caseInfo.setUrl(regexReplace(url));
        //3、參數輸入
        String inputParams = caseInfo.getInputParams();
        caseInfo.setInputParams(regexReplace(inputParams));
        //4、期望結果
        String expected = caseInfo.getExpected();
        caseInfo.setExpected(regexReplace(expected));
        return caseInfo;
    }


}

自此,我們的BaseTest已經封裝完畢,后邊我們每一個測試類都繼承該BaseTest類,很大程度降低了代碼了耦合度,接下來,我們在testcases包下新建一個Test02測試類,試下發起請求是否成功:

package com.lrc.testcases;

import com.lrc.common.BaseTest;
import com.lrc.config.Contants;
import com.lrc.config.Environment;
import com.lrc.entries.CaseInfo;
import com.lrc.utils.EasyPoiExcelUtil;
import io.restassured.response.Response;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.util.List;

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description
 **/
public class Test02 extends BaseTest {
    @BeforeClass
    public void setUp(){
        //向環境變量設置key
        Environment.envMap.put("key",Contants.KEY);
    }

    @Test(dataProvider = "readCases")
    public void test01(CaseInfo caseInfo){
        //將測試用例做整體替換,只要遇到${}數據,就替換為環境變量中的實際數據
        caseInfo=paramsReplace(caseInfo);
        //發起請求
        Response res = request(caseInfo);
        //斷言請求
        assertResponse(res,caseInfo);
        //將測試用例的提取表達式保存到環境變量中
        extractToEnvironment(res,caseInfo);
    }



    @DataProvider
    //向測試用例提供Excel數據
    public Object[] readCases(){
        List<CaseInfo> listDatas = EasyPoiExcelUtil.readExcel(0);
        return listDatas.toArray();
    }



}

 

執行結果:

成功根據我們的Excel用例輸出結果。

 

四:數據庫斷言操作

在做接口測試的時候,經常需要結合數據庫進行斷言,提高測試用例的正確性,由於本次框架的免費API文檔拿不到官方數據庫信息,此處只做出介紹,不實際運行,大家可以參考到自己的實際項目當中。

1、在pom.xml中添加數據庫操作包依賴:

<!-- mysql數據庫驅動 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>
<!-- 數據庫連接工具包 -->
<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.6</version>
</dependency>

2、在utils包下新建JDBCUtils工具類,編寫數據庫的操作

package com.lrc.utils;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description
 **/
public class JDBCUtils {
    /**
     * 和數據庫建立連接
     * @return 數據庫連接對象
     */
    public static Connection getConnection()  {
        //定義數據庫連接
        //Oracle:jdbc:oracle:thin:@localhost:1521:DBName
        //SqlServer:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=DBName
        //MySql:jdbc:mysql://localhost:3306/DBName
        String url="jdbc:mysql://xxxx?useUnicode=true&characterEncoding=utf-8";
        String user="xxxx";
        String password="xxxx";
        //定義數據庫連接對象
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url, user,password);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return conn;
    }


    /**
     * 修改數據庫數據操作(插入、修改、刪除)
     * @param sql 要執行的sql語句
     */
    public static void updateData(String sql){
        //1、建立連接
        Connection conn = getConnection();
        //2、QueryRunner對象生成
        QueryRunner queryRunner = new QueryRunner();
        //3、執行sql
        try {
            queryRunner.update(conn,sql);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            //關閉連接
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

    /**
     * 查詢單個字段的數據
     * @param sql 要執行的sql語句
     * @return 返回查詢結果
     */
    public static Object querySingleData(String sql){
        //1、建立連接
        Connection conn = getConnection();
        //2、QueryRunner對象生成
        QueryRunner queryRunner = new QueryRunner();
        //3、執行sql
        Object data =null ;
        try {
            data = queryRunner.query(conn,sql,new ScalarHandler<Object>());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return data;
    }

    /**
     * 查詢所有的數據
     * @param sql 要執行的sql語句
     * @return 返回查詢結果
     */
    public static List<Map<String,Object>> queryAllDatas(String sql){
        //1、建立連接
        Connection conn = getConnection();
        //2、QueryRunner對象生成
        QueryRunner queryRunner = new QueryRunner();
        //3、執行sql
        List<Map<String,Object>> data = null;
        try {
            data = queryRunner.query(conn,sql,new MapListHandler());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return data;
    }
}

3、在BaseTest類中添加數據庫斷言方法封裝

/**
 * 數據庫斷言統一封裝
 * @param caseInfo 用例數據
 */
public void assertDB(CaseInfo caseInfo){
    String dbAssertInfo = caseInfo.getDbAssert();
    if(dbAssertInfo != null) {
        Map<String, Object> mapDbAssert = JSONObject.parseObject(dbAssertInfo);
        Set<String> allKeys = mapDbAssert.keySet();
        for (String key : allKeys) {
            //key為對應要執行的sql語句
            Object dbActual = JDBCUtils.querySingleData(key);
            //根據數據庫中讀取實際返回類型做判斷
            //1、Long類型
            if(dbActual instanceof Long){
                Integer dbExpected = (Integer) mapDbAssert.get(key);
                Long expected = dbExpected.longValue();
                Assert.assertEquals(dbActual, expected);
            }else {
                Object expected = mapDbAssert.get(key);
                Assert.assertEquals(dbActual, expected);
            }
        }
    }
}

4、后邊在需要做數據庫斷言的測試用例中,只需要調用該assertDB方法即可。

 

五:報表集成

1、在pom.xml中添加allure報表依賴:

<!--allure報表依賴-->
<dependency>
    <groupId>io.qameta.allure</groupId>
    <artifactId>allure-testng</artifactId>
    <version>2.12.1</version>
    <scope>test</scope>
</dependency>

2、在pom.xml的<project>標簽下覆蓋<properties>標簽

<properties>
    <aspectj.version>1.8.10</aspectj.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>

3、在pom.xml的<project>標簽下級中添加build標簽

<build>
    <plugins>
        <plugin>
            <!-- maven-surefire-plugin 配合testng執行測試用例的maven插件 -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.1</version>
            <configuration>
                <!-- 測試失敗后,是否忽略並繼續測試 -->
                <testFailureIgnore>true</testFailureIgnore>
                <suiteXmlFiles>
                    <!-- testng配置文件名稱 -->
                    <suiteXmlFile>testng02.xml</suiteXmlFile>
                </suiteXmlFiles>
                <!--設置參數命令行 -->
                <argLine>
                    <!-- UTF-8編碼 -->
                    -Dfile.encoding=UTF-8
                    <!-- 配置攔截器 -->
                    -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                </argLine>
                <systemProperties>
                    <property>
                        <!-- 配置 allure 結果存儲路徑 -->
                        <name>allure.results.directory</name>
                        <value>${project.build.directory}/allure-results</value>
                    </property>
                </systemProperties>
            </configuration>
            <dependencies>
                <!-- aspectjweaver maven坐標 -->
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjweaver</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
            </dependencies>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>8</source>
                <target>8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

4、至此,Allure報表的集成操作已經完成了,接下來就可以使用Allure報表生成測試報告。

通過Allure報表生成報告的操作:

(1)在工程目錄下新建個testng.xml文件,此處的文件需要與上述Maven Surefire插件配置的testng.xml文件名一致,填入如下信息:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="測試套件" >
    <test name="測試">
        <classes>
            <class name="com.lemon.testcases.RegisterTest"/>
            <class name="com.lemon.testcases.LoginTest"/>
            <class name="com.lemon.testcases.GetUserInfoTest"/>
            <class name="com.lemon.testcases.InvestFlowTest"/>
        </classes>
    </test>
</suite>

其中的class是測試用例的類名,文件放置的目錄如下圖:

(2)在命令行執行命令:

mvn clean test

注意:必須使用maven構建測試執行,不能直接在測試類中執行或者在testng.xml中右鍵執行,那樣是生成不了allure報表的。

(3)生成allure報表:

mvn io.qameta.allure:allure-maven:serve

 

生成了allure報表:

六:日志集成

1、在之前介紹過全局配置類Contants中添加一個控制台日志開關控制權限,如果選擇為false,則控制台不輸出日志,將日志輸出到allure報表,選擇為true,則在控制台輸出日志,不輸出到allure報表

//控制台日志輸出開關(true->輸出到控制台,false->不輸出到控制台)
public static final boolean SHOW_CONSOLE_LOG=false;

2、在BaseTest類中的封裝好的request方法添加日志輸出控制邏輯:

/**
 * 封裝所有請求類型
 * @param caseInfo 測試用例對象
 * @return response響應對象
 */
public static Response request(CaseInfo caseInfo){
    //在用例基類每個請求添加日志
    String logFilepath="";
    //如果開關控制為false,即不在控制台輸出日志,才創建日志文件
    if(!Contants.SHOW_CONSOLE_LOG) {
        //此處按照接口名稱進行日志文件分類處理
        File dirFile = new File("logs\\" + caseInfo.getInterfaceName());
        if (!dirFile.exists()) {
            //如果文件及文件夾不存在,則創建文件及文件夾
            dirFile.mkdirs();
        }
        PrintStream fileOutPutStream = null;
        //日志文件路徑
        logFilepath = "logs\\" + caseInfo.getInterfaceName() + "\\" + caseInfo.getInterfaceName() + "_" + caseInfo.getCaseId() + ".log";
        try {
            fileOutPutStream = new PrintStream(new File(logFilepath));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //每個接口請求的日志單獨的保存到本地的每一個文件中
        RestAssured.config = RestAssured.config().logConfig(LogConfig.logConfig().defaultStream(fileOutPutStream));
    }
    String requestHeaders=caseInfo.getRequestHeader();
    Map requestHeadersMap= JSONObject.parseObject(requestHeaders);
    String url=caseInfo.getUrl();
    String params=caseInfo.getInputParams();
    String method=caseInfo.getMethod();
    Response response=null;
    if ("get".equalsIgnoreCase(method)) {
        response = RestAssured.given().log().all().headers(requestHeadersMap).when().get(url).then().log().all().extract().response();
    }
    else if ("post".equalsIgnoreCase(method)) {
        response = RestAssured.given().log().all().headers(requestHeadersMap).body(params).when().post(url).then().log().all().extract().response();
    }
    else if ("put".equalsIgnoreCase(method)) {
        response = RestAssured.given().log().all().headers(requestHeadersMap).body(params).when().post(url).then().log().all().extract().response();
    }
    //可以在此處添加想要的信息到日志文件中
    //請求結束之后將接口日志添加到allure報表中
    if(!Contants.SHOW_CONSOLE_LOG) {
        try {
            Allure.addAttachment("接口請求響應日志", new FileInputStream(logFilepath));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    return response;
}

3、自此,日志集成已經完成,我們在配置類Contants將SHOW_CONSOLE_LOG定義成false,將會在報表中可以查看日志:

而當我們將SHOW_CONSOLE_LOG定義成true的時候,就可以在控制台輸出日志調試,不會輸出到報表。

 

七:最后一個環節了,通過GitLab管理我們的項目代碼,並提交到Jenkins持續集成部署,日后有時間會繼續更新。

 

文章末尾附上項目源碼:

鏈接:https://pan.baidu.com/s/126_01gLPINoGMd0mR4PGOA 
提取碼:jkk2 

 

附言:文章編寫不易,覺得本文寫的不錯的可以點點贊,關注一下,有問題的也可以留言討論下!!!

搭建過程中,如果大家遇到什么困難,可以加v提出問題:751964382

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM