基於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
附言:文章編寫不易,覺得本文寫的不錯的可以點點贊,關注一下,有問題的也可以留言討論下!!!