本次介紹的框架使用“聚合數據”的免費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
附言:文章編寫不易,覺得本文寫的不錯的可以點點贊,關注一下,有問題的也可以留言討論下!!!
搭建過程中,如果大家遇到什么困難,可以加v提出問題:751964382