基于Java+Maven+Testng+RestAssured+Allure+Jenkins搭建一个接口自动化框架

本次介绍的框架使用“聚合数据”的免费API作为实例进行框架介绍,在介绍框架搭建前,可以先注册一个聚合数据账号进行实操。

一:聚合数据的注册:

1、百度搜索“聚合数据”,进入到聚合数据官网注册个账号。
2、注册完账号后,点击【API】导航即可选用免费的试用接口

image

 

3、选用想用的API分类,本次框架以【新闻头条】为例

image

 

image

 

image

至此就完成了接口申请,可以在【数据中心】->我的API中查看接口的信息,后边的框架介绍也是基于该接口API进行接口测试

 

 

二:测试用例的设计

测试用例的设计巧妙之处:

1、参照了Jmeter的参数化方式,将需要参数化的数据用${}包裹起来,后边在解析Excel时,对${}包裹起来的数据,用正则表达式等技术手段替换成实际环境变量的数据,从而实现了参数化设计

2、有一个提取表达式列,通过编写每个接口的JSON提取表达式,后边在解析Excel时,对接口执行后的响应数据用该表达式提取出来,保存到环境变量,如果某些接口需要前置接口的响应数据,我们就可以从环境变量中获取该数据出来,从而解决了接口关联的问题。

3、有一个数据库断言列,由于接口通常需要与数据库操作关联起来,那么通过数据库断言,可以使接口测试结果更加准确稳定。

 

接下来就进行实际框架搭建了。

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

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

本次框架主要包:

common包:存放测试用例的基类,用于封装测试用例的共性操作

config包:存放项目的配置信息,像项目的域名信息、测试用例Excel路径等会在此处配置

enties包:存放项目的实体类

testcases包:存放测试用例

utils包:存放项目的工具类

二:我们现在先来解决测试用例的读取操作。

1、在src/test目录下新建一个resources资源目录,将测试用例放到该目录下

2、在entries包下新建一个CaseInfo实体类,实体类属性编写我们Excel测试用例所有列信息,每个属性用@Excel注解标注,该注解主要是EasyPOI可以用来映射对象属性信息,注意:@Excel里的name信息要与表头字段完全一致,否则无法映射。然后每个属性添加getter、setter方法,最后再添加一个toString方法。

package com.lrc.entries;

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

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

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

    @Excel(name = "用例标题(title)")
    private String title;

    @Excel(name = "请求头(requestHeader)")
    private String requestHeader;

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

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

    @Excel(name = "参数输入(inputParams)")
    private String inputParams;

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

    @Excel(name = "数据库断言")
    private String dbAssert;

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

    public int getCaseId() {
        return caseId;
    }

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

    public String getInterfaceName() {
        return interfaceName;
    }

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

    public String getTitle() {
        return title;
    }

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

    public String getRequestHeader() {
        return requestHeader;
    }

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

    public String getMethod() {
        return method;
    }

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

    public String getUrl() {
        return url;
    }

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

    public String getInputParams() {
        return inputParams;
    }

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

    public String getExpected() {
        return expected;
    }

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

    public String getDbAssert() {
        return dbAssert;
    }

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

    public String getExtractExper() {
        return extractExper;
    }

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

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

3、在config包下新建Contants类,填写项目的基本配置信息

package com.lrc.config;

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description 项目常规信息配置类
 **/
public class Contants {
    //项目访问地址
    public static final String PROJECT_URL="v.juhe.cn";
    //项目BASEURI地址
    public static final String BASE_URL="http://"+PROJECT_URL;
    //测试用例路径
    public static final String EXCEL_PATH="src\\test\\resources\\api_testcases.xls";
    //账号数据的Key,此处的key是自己申请聚合数据账号后得到的key
    public static final String KEY="xxxxx";
}

4、在utils包下新建POI操作类EasyPoiExcelUtil,用于解析Excel测试用例

package com.lrc.utils;

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

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

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description Excel解析工具类
 **/
public class EasyPoiExcelUtil {
    /**
     * 使用EasyPOI读取Excel数据
     * @return 用例list集合
     * 获取Excel里的所有行
     */
    public static List<CaseInfo> readExcel(int num){
        //读取测试用例
        File file=new File(Contants.EXCEL_PATH);
        //读取和导入Excel的参数配置
        ImportParams params=new ImportParams();
        params.setStartSheetIndex(num);
        //读取测试用例整合成每条用例对象集合
        List<CaseInfo> cases = ExcelImportUtil.importExcel(file, CaseInfo.class, params);
        return cases;
    }


    /**
     * 使用EasyPOI读取Excel数据
     * @return 用例list集合
     * 获取Excel里的指定行
     */
    public static List<CaseInfo> readExcelPart(int num,int startNum,int readRows){
        //读取测试用例
        File file=new File(Contants.EXCEL_PATH);
        //读取和导入Excel的参数配置
        ImportParams params=new ImportParams();
        //读取指定页的Sheet
        params.setStartSheetIndex(num);
        //指定从第几行开始读取
        params.setStartRows(startNum);
        //指定读取几行数据
        params.setReadRows(readRows);
        //读取测试用例整合成每条用例对象集合
        List<CaseInfo> cases = ExcelImportUtil.importExcel(file, CaseInfo.class, params);
        return cases;
    }

    /**
     * 使用EasyPOI读取Excel数据
     * @return 用例list集合
     * 从指定行开始读取下面全部用例
     */
    public static List<CaseInfo> readExcelPart(int num,int startNum){
        //读取测试用例
        File file=new File(Contants.EXCEL_PATH);
        //读取和导入Excel的参数配置
        ImportParams params=new ImportParams();
        //读取指定页的Sheet
        params.setStartSheetIndex(num);
        //指定从第几行开始读取
        params.setStartRows(startNum);
        //读取测试用例整合成每条用例对象集合
        List<CaseInfo> cases = ExcelImportUtil.importExcel(file, CaseInfo.class, params);
        return cases;
    }
}

5、我们在testcases包下新建一个Test01类测试下是否能够解析Excel

package com.lrc.testcases;

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

import java.util.List;

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

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

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

执行Test01结果,成功读取了当前Excel中编写的4条用例:

自此,Excel用例的读取操作已经完美解决了。

 

三:接下来讲接口的通用常规操作封装到common包下的BaseTest类中:

1、首先在config包下新建一个Environment类,作为环境变量的接收传递类

package com.lrc.config;

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

/**
 * @param
 * @author lrc
 * @create 2022/1/9
 * @return
 * @description 主要用于全局管理环境变量,模拟Jmeter变量存储操作
 **/
public class Environment {
    //声明并定义一个map(类似于JMeter的环境变量)
    public static Map<String,Object> envMap = new HashMap<String, Object>();
}

2、在BaseTest中编写测试用例的共性操作方法:

package com.lrc.common;

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


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

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

    @BeforeSuite
    public void beforeMethod(){
        //把json小数的返回类型配置成BigDecimal类型,通过此配置,可以使得我们在断言小数类型的时候保持数据类型一致,避免了因数据类型不一致而导致断言不通过的情况
        RestAssured.config = RestAssured.config().jsonConfig(JsonConfig.jsonConfig().numberReturnType(JsonPathConfig.NumberReturnType.BIG_DECIMAL));
        //REST-assured基础 baseurl设置
        RestAssured.baseURI= Contants.BASE_URL;
    }

    /**
     * 封装所有请求类型
     * @param caseInfo 测试用例对象
     * @return response响应对象
     */
    public static Response request(CaseInfo caseInfo){
        //读取测试用例的请求头
        String requestHeaders=caseInfo.getRequestHeader();
        //将请求头转为map类型数据
        Map requestHeadersMap= JSONObject.parseObject(requestHeaders);
        //读取测试用例的url
        String url=caseInfo.getUrl();
        //读取测试用例的body输入参数
        String params=caseInfo.getInputParams();
        //读取测试用例的请求方式
        String method=caseInfo.getMethod();
        //封装请求方法
        Response response=null;
        if ("get".equalsIgnoreCase(method)) {
            response = RestAssured.given().log().all().headers(requestHeadersMap).when().get(url).then().log().all().extract().response();
        }
        else if ("post".equalsIgnoreCase(method)) {
            response = RestAssured.given().log().all().headers(requestHeadersMap).body(params).when().post(url).then().log().all().extract().response();
        }
        else if ("put".equalsIgnoreCase(method)) {
            response = RestAssured.given().log().all().headers(requestHeadersMap).body(params).when().post(url).then().log().all().extract().response();
        }
     
        return response;
    }

    /**
     * 响应断言
     * @param res 实际响应结果
     * @param caseInfo 请求数据(实体类)
     */
    public void assertResponse(Response res,CaseInfo caseInfo){
        String expected = caseInfo.getExpected();
        if(expected != null) {
            //转成Map
            Map<String, Object> expectedMap = JSONObject.parseObject(expected);
            Set<String> allKeySet = expectedMap.keySet();
            for (String key : allKeySet) {
                //获取实际响应结果
                Object actualResult = res.jsonPath().get(key);
                //获取期望结果
                Object expectedResult = expectedMap.get(key);
                Assert.assertEquals(actualResult, expectedResult);
            }
        }
    }

    /**
     * 数据库断言统一封装
     * @param caseInfo 用例数据
     */
    public void assertDB(CaseInfo caseInfo){
        String dbAssertInfo = caseInfo.getDbAssert();
        if(dbAssertInfo != null) {
            Map<String, Object> mapDbAssert = JSONObject.parseObject(dbAssertInfo);
            Set<String> allKeys = mapDbAssert.keySet();
            for (String key : allKeys) {
                //key为对应要执行的sql语句
                Object dbActual = JDBCUtils.querySingleData(key);
                //根据数据库中读取实际返回类型做判断
                //1、Long类型
                if(dbActual instanceof Long){
                    Integer dbExpected = (Integer) mapDbAssert.get(key);
                    Long expected = dbExpected.longValue();
                    Assert.assertEquals(dbActual, expected);
                }else {
                    Object expected = mapDbAssert.get(key);
                    Assert.assertEquals(dbActual, expected);
                }
            }
        }
    }


    /**
     * 通过【提取表达式】将对应响应值保存到环境变量中
     * @param res 响应信息
     * @param caseInfo 实体类对象
     */
    public void extractToEnvironment(Response res, CaseInfo caseInfo){
        String extractStr = caseInfo.getExtractExper();
        if(extractStr != null) {
            //把提取表达式转成Map
            Map<String, Object> map = JSONObject.parseObject(extractStr);
            Set<String> allKeySets = map.keySet();
            for (String key : allKeySets) {
                //key为变量名,value是为提取的gpath表达式
                Object value = map.get(key);
                Object actualValue = res.jsonPath().get((String) value);
                //将对应的键和值保存到环境变量中
                Environment.envMap.put(key, actualValue);
            }
        }
    }


    /**
     * 正则替换功能,比如:
     * 原始字符串 {
     *   key=${key}
     * }
     * 替换为
     * {
     *   key=xxxx(自己账号生成的key)
     * }
     * xxxx 为环境变量中key变量名对应的变量值
     * @param orgStr 源字符串
     * @return
     */
    public String regexReplace(String orgStr){
        if(orgStr != null) {
            //匹配器
            Pattern pattern = Pattern.compile("\\$\\{(.*?)\\}");
            //匹配对象
            Matcher matcher = pattern.matcher(orgStr);
            String result = orgStr;
            //循环遍历匹配对象
            while (matcher.find()) {
                //获取整个匹配正则的字符串 ${key}
                String allFindStr = matcher.group(0);
                //找到${XXX}内部的匹配的字符串 key
                String innerStr = matcher.group(1);
                //找到key:xxxx
                //具体的要替换的值(从环境变量中去找到的)
                Object replaceValue = Environment.envMap.get(innerStr);
                //要替换${key} --> xxxx
                result = result.replace(allFindStr, replaceValue + "");
            }
            return result;
        }else{
            return orgStr;
        }
    }

    /**
     * 整条用例数据的参数化替换,只要在对应的用例数据里面有${}包裹起来的数据,那么就会从环境变量中找,如果找到的话就去替换,否则不会
     * @param caseInfo
     */
    public CaseInfo paramsReplace(CaseInfo caseInfo){
        //1、请求头
        String requestHeader = caseInfo.getRequestHeader();
        caseInfo.setRequestHeader(regexReplace(requestHeader));
        //2、接口地址
        String url = caseInfo.getUrl();
        caseInfo.setUrl(regexReplace(url));
        //3、参数输入
        String inputParams = caseInfo.getInputParams();
        caseInfo.setInputParams(regexReplace(inputParams));
        //4、期望结果
        String expected = caseInfo.getExpected();
        caseInfo.setExpected(regexReplace(expected));
        return caseInfo;
    }


}

自此,我们的BaseTest已经封装完毕,后边我们每一个测试类都继承该BaseTest类,很大程度降低了代码了耦合度,接下来,我们在testcases包下新建一个Test02测试类,试下发起请求是否成功:

package com.lrc.testcases;

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

import java.util.List;

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

    @Test(dataProvider = "readCases")
    public void test01(CaseInfo caseInfo){
        //将测试用例做整体替换,只要遇到${}数据,就替换为环境变量中的实际数据
        caseInfo=paramsReplace(caseInfo);
        //发起请求
        Response res = request(caseInfo);
        //断言请求
        assertResponse(res,caseInfo);
        //将测试用例的提取表达式保存到环境变量中
        extractToEnvironment(res,caseInfo);
    }



    @DataProvider
    //向测试用例提供Excel数据
    public Object[] readCases(){
        List<CaseInfo> listDatas = EasyPoiExcelUtil.readExcel(0);
        return listDatas.toArray();
    }



}

 

执行结果:

成功根据我们的Excel用例输出结果。

 

四:数据库断言操作

在做接口测试的时候,经常需要结合数据库进行断言,提高测试用例的正确性,由于本次框架的免费API文档拿不到官方数据库信息,此处只做出介绍,不实际运行,大家可以参考到自己的实际项目当中。

1、在pom.xml中添加数据库操作包依赖:

<!-- mysql数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>
<!-- 数据库连接工具包 -->
<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.6</version>
</dependency>

2、在utils包下新建JDBCUtils工具类,编写数据库的操作

package com.lrc.utils;

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

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

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


    /**
     * 修改数据库数据操作(插入、修改、删除)
     * @param sql 要执行的sql语句
     */
    public static void updateData(String sql){
        //1、建立连接
        Connection conn = getConnection();
        //2、QueryRunner对象生成
        QueryRunner queryRunner = new QueryRunner();
        //3、执行sql
        try {
            queryRunner.update(conn,sql);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            //关闭连接
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }

    /**
     * 查询单个字段的数据
     * @param sql 要执行的sql语句
     * @return 返回查询结果
     */
    public static Object querySingleData(String sql){
        //1、建立连接
        Connection conn = getConnection();
        //2、QueryRunner对象生成
        QueryRunner queryRunner = new QueryRunner();
        //3、执行sql
        Object data =null ;
        try {
            data = queryRunner.query(conn,sql,new ScalarHandler<Object>());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return data;
    }

    /**
     * 查询所有的数据
     * @param sql 要执行的sql语句
     * @return 返回查询结果
     */
    public static List<Map<String,Object>> queryAllDatas(String sql){
        //1、建立连接
        Connection conn = getConnection();
        //2、QueryRunner对象生成
        QueryRunner queryRunner = new QueryRunner();
        //3、执行sql
        List<Map<String,Object>> data = null;
        try {
            data = queryRunner.query(conn,sql,new MapListHandler());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return data;
    }
}

3、在BaseTest类中添加数据库断言方法封装

/**
 * 数据库断言统一封装
 * @param caseInfo 用例数据
 */
public void assertDB(CaseInfo caseInfo){
    String dbAssertInfo = caseInfo.getDbAssert();
    if(dbAssertInfo != null) {
        Map<String, Object> mapDbAssert = JSONObject.parseObject(dbAssertInfo);
        Set<String> allKeys = mapDbAssert.keySet();
        for (String key : allKeys) {
            //key为对应要执行的sql语句
            Object dbActual = JDBCUtils.querySingleData(key);
            //根据数据库中读取实际返回类型做判断
            //1、Long类型
            if(dbActual instanceof Long){
                Integer dbExpected = (Integer) mapDbAssert.get(key);
                Long expected = dbExpected.longValue();
                Assert.assertEquals(dbActual, expected);
            }else {
                Object expected = mapDbAssert.get(key);
                Assert.assertEquals(dbActual, expected);
            }
        }
    }
}

4、后边在需要做数据库断言的测试用例中,只需要调用该assertDB方法即可。

 

五:报表集成

1、在pom.xml中添加allure报表依赖:

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

2、在pom.xml的<project>标签下覆盖<properties>标签

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

3、在pom.xml的<project>标签下级中添加build标签

<build>
    <plugins>
        <plugin>
            <!-- maven-surefire-plugin 配合testng执行测试用例的maven插件 -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.22.1</version>
            <configuration>
                <!-- 测试失败后,是否忽略并继续测试 -->
                <testFailureIgnore>true</testFailureIgnore>
                <suiteXmlFiles>
                    <!-- testng配置文件名称 -->
                    <suiteXmlFile>testng02.xml</suiteXmlFile>
                </suiteXmlFiles>
                <!--设置参数命令行 -->
                <argLine>
                    <!-- UTF-8编码 -->
                    -Dfile.encoding=UTF-8
                    <!-- 配置拦截器 -->
                    -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                </argLine>
                <systemProperties>
                    <property>
                        <!-- 配置 allure 结果存储路径 -->
                        <name>allure.results.directory</name>
                        <value>${project.build.directory}/allure-results</value>
                    </property>
                </systemProperties>
            </configuration>
            <dependencies>
                <!-- aspectjweaver maven坐标 -->
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjweaver</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
            </dependencies>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>8</source>
                <target>8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

4、至此,Allure报表的集成操作已经完成了,接下来就可以使用Allure报表生成测试报告。

通过Allure报表生成报告的操作:

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

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

其中的class是测试用例的类名,文件放置的目录如下图:

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

mvn clean test

注意:必须使用maven构建测试执行,不能直接在测试类中执行或者在testng.xml中右键执行,那样是生成不了allure报表的。

(3)生成allure报表:

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

 

生成了allure报表:

六:日志集成

1、在之前介绍过全局配置类Contants中添加一个控制台日志开关控制权限,如果选择为false,则控制台不输出日志,将日志输出到allure报表,选择为true,则在控制台输出日志,不输出到allure报表

//控制台日志输出开关(true->输出到控制台,false->不输出到控制台)
public static final boolean SHOW_CONSOLE_LOG=false;

2、在BaseTest类中的封装好的request方法添加日志输出控制逻辑:

/**
 * 封装所有请求类型
 * @param caseInfo 测试用例对象
 * @return response响应对象
 */
public static Response request(CaseInfo caseInfo){
    //在用例基类每个请求添加日志
    String logFilepath="";
    //如果开关控制为false,即不在控制台输出日志,才创建日志文件
    if(!Contants.SHOW_CONSOLE_LOG) {
        //此处按照接口名称进行日志文件分类处理
        File dirFile = new File("logs\\" + caseInfo.getInterfaceName());
        if (!dirFile.exists()) {
            //如果文件及文件夹不存在,则创建文件及文件夹
            dirFile.mkdirs();
        }
        PrintStream fileOutPutStream = null;
        //日志文件路径
        logFilepath = "logs\\" + caseInfo.getInterfaceName() + "\\" + caseInfo.getInterfaceName() + "_" + caseInfo.getCaseId() + ".log";
        try {
            fileOutPutStream = new PrintStream(new File(logFilepath));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //每个接口请求的日志单独的保存到本地的每一个文件中
        RestAssured.config = RestAssured.config().logConfig(LogConfig.logConfig().defaultStream(fileOutPutStream));
    }
    String requestHeaders=caseInfo.getRequestHeader();
    Map requestHeadersMap= JSONObject.parseObject(requestHeaders);
    String url=caseInfo.getUrl();
    String params=caseInfo.getInputParams();
    String method=caseInfo.getMethod();
    Response response=null;
    if ("get".equalsIgnoreCase(method)) {
        response = RestAssured.given().log().all().headers(requestHeadersMap).when().get(url).then().log().all().extract().response();
    }
    else if ("post".equalsIgnoreCase(method)) {
        response = RestAssured.given().log().all().headers(requestHeadersMap).body(params).when().post(url).then().log().all().extract().response();
    }
    else if ("put".equalsIgnoreCase(method)) {
        response = RestAssured.given().log().all().headers(requestHeadersMap).body(params).when().post(url).then().log().all().extract().response();
    }
    //可以在此处添加想要的信息到日志文件中
    //请求结束之后将接口日志添加到allure报表中
    if(!Contants.SHOW_CONSOLE_LOG) {
        try {
            Allure.addAttachment("接口请求响应日志", new FileInputStream(logFilepath));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    return response;
}

3、自此,日志集成已经完成,我们在配置类Contants将SHOW_CONSOLE_LOG定义成false,将会在报表中可以查看日志:

而当我们将SHOW_CONSOLE_LOG定义成true的时候,就可以在控制台输出日志调试,不会输出到报表。

 

七:最后一个环节了,通过GitLab管理我们的项目代码,并提交到Jenkins持续集成部署,日后有时间会继续更新。

 

文章末尾附上项目源码:

链接:https://pan.baidu.com/s/126_01gLPINoGMd0mR4PGOA 
提取码:jkk2 

 

附言:文章编写不易,觉得本文写的不错的可以点点赞,关注一下,有问题的也可以留言讨论下!!!

搭建过程中,如果大家遇到什么困难,可以加v提出问题:751964382

 

posted @ 2022-01-09 08:39  筱筱创  阅读(2906)  评论(11编辑  收藏  举报