1. 程式人生 > 其它 >基於Java+Maven+Testng+RestAssured+Allure+Jenkins搭建一個介面自動化框架

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

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

一:聚合資料的註冊:

1、百度搜索“聚合資料”,進入到聚合資料官網註冊個賬號。
2、註冊完賬號後,點選【API】導航即可選用免費的試用介面

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

至此就完成了介面申請,可以在【資料中心】->我的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

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