本次介绍的框架使用“聚合数据”的免费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
附言:文章编写不易,觉得本文写的不错的可以点点赞,关注一下,有问题的也可以留言讨论下!!!