您当前的位置: 首页 >  自动化

测试萌萌

暂无认证

  • 2浏览

    0关注

    1003博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

基于TestNG+Rest Assured+Allure的接口自动化测试框架

测试萌萌 发布时间:2021-04-06 16:21:47 ,浏览量:2

一、前言

当今,“自动化测试”大行其道,其中“接口自动化测试”便是同行们谈得最多的话题之一。了解测试金字塔分层理念的童鞋都清楚,接口自动化测试有以下优点。

  • 投入低,产出高。
  • 比UI自动化更稳定。
  • 比单元测试更接近真实业务。

正因为以上优点,接口自动化测试逐渐成为了业界主流,各种工具/框架层出不穷,比如Postman,Jmeter,Htttpclient,Rest assured,HttpRunnerManager等。

二、背景 此前笔者曾基于Jenkins+Ant+Git+Jmeter搭建过一套接口自动化框架,期间亦针对Jmeter做了许多功能的扩展,比如:生成excle结果文件、数据库断言、自动提交缺陷、自动更新案例执行结果至Testlink等。虽说Jmeter简单易上手,但在笔者看来,其并不是接口自动化测试的首选,其中的原因暂不祥谈,毕竟仁者见仁。 近段时间,笔者一直在思索,学习前辈们优秀的经验,并从公司项目架构出发,搭建了一套基于Jenkins+Maven+Git+TestNG+RestAssured+Allure的持续集成测试框架,相比原先Jmeter的那套,其易用性更好、效率更高、扩展性更强。

三、框架理念

  • 接口自动化测试框架
  • 根据用例模板编写测试用例(包含响应报文断言及接口入参等)。
  • 编写数据库断言文件。
  • 编写测试类。
  • 配置环境及测试用例集。
  • Jenkins选择运行环境及用例集,触发构建。
  • 生成测试报告,邮件通知相关人员。
四、操作步骤 1、编写用例

用例文件名称与测试类名一致,比如开户的测试类名为OpenAcc,则用例文件名为OpenAcc.xls,用例模板由以下几部分组成。

  • caseNo、testPoint分别与案例管理系统的案例编号、案例标题对应,方便后续同步执行结果。
  • preResult为响应报文断言,目前支持jsonpath、包含断言、不包含断言。
  • YorN为案例执行标志,Y表示执行。
  • tableCheck为数据库断言标志,Y表示进行数据库断言。
  • 其他字段为接口入参,目前支持以下五种供数方式。

(1)自定义函数供数。引用格式为:__phone()表示生成11位手机号。__idno()表示生成18位身份证号。

(2)查询数据池供数。引用格式为:${dp.sql(select accountNo from M_account where status = 1)}

(3)查询数据库供数。引用格式为:${db.sql(select accountNo from M_account_card where status = 1)}

(4)先接口请求,然后提取响应报文供数。引用格式为:KaTeX parse error: Expected '}', got 'EOF' at end of input: …g.case023.post(.data.code)},表示先以post方式发送SendmsgYg接口请求,然后再提取响应报文的code字段。支持接口之间的多重依赖。

(5)先接口请求,然后查询数据库/池供数。引用格式为:${SendmsgYg.case023.post.db.sql(select accountNo from M_account_card where status = 1)},表示先以post方式发送SendmsgYg接口请求,然后再查询数据库(db)/数据池(dp)获取数据。

2、编写数据库断言

数据库断言文件名称与测试类名一致,比如开户的测试类名为OpenAcc,则断言文件为OpenAcc.xml。一个接口对应一个数据库断言文件,一个断言文件里可包含多条案例,每条案例可以断言多张表,模板如下。 ps:为了提升效率,后续亦会提供一个生成数据库断言文件小工具。




     
         
            ACCOUNT_NO      
            CUST_ID         
            MERCHANT_ID 
            1        
            0019901        
        
    

    
        
            ACCOUNT_NO
            CARD_NO
            2
            MERCHANT_ID
            CUST_ID
            CARD_IMG
            NOTNULL
        
    

对于未确定的预期结果,使用变量代替,后续编写测试类时再做映射。

3、编写测试类

测试类分为两大类,一是只需响应报文断言,二是需要响应报文及数据库断言。测试人员按照以下模板编写脚本即可。

/*
 *短信发送接口
 * 环境参数在SetUpTearDown 父类定义
 */
@Feature("分类账户改造")
public class SendmsgYg extends SetUpTearDown {

    @Story("发送短信")
    @Test(dataProvider = "dataprovider",
            dataProviderClass = DataProviders.class,
            description = "发送短信")
    public void runCase(String caseMess, String bodyString) throws IOException, SQLException, ClassNotFoundException {

        //发送请求
        Response response = RunCaseJson.runCase(bodyString, "post");

        //只进行响应报文断言
        asserts(caseMess, bodyString, response.asString(),"",null);
    }
}

数据库断言文件中的变量,可通过调用封装的方法取值,比如查数据库、提取响应报文、调用接口等方式。

/*
 *开立分类账户
 * 环境参数在SetUpTearDown 父类定义
 */
@Feature("分类账户改造")
public class OpenYg extends SetUpTearDown {

    @Story("分类账户开户")
    @Test(dataProvider = "dataprovider",
            dataProviderClass = DataProviders.class,
            description = "开户")
    public void runCase(String caseMess, String bodyString) throws IOException, SQLException, ClassNotFoundException {

        //发送请求
        Response response = RunCaseJson.runCase(bodyString, "post");

        //如果需要数据库断言,此处添加断言文件变量的map映射
        //可通过调用封装的方法取值,比如查数据库、提取响应报文、调用接口等方式。
        Map map = new HashMap();
        //查询数据库获取,取不到值返回""
        String account = DataBaseCRUD.selectData("select accountNo from M_ACCOUNT where status =1");
        //提取响应报文,取不到值返回""
        String custId = RespondAssertForJson.getBuildValue(response.asString(),"$.data.custid");
        //执行SendmsgYg接口的case023案例,然后提取响应报文的merchanId ,取不到值返回""
        String merchanId = RespondAssertForJson.getBuildValue("","${SendmsgYg.case023.post($.data.merchanId)}");

        map.put("ACCOUNT_NO",account);
        map.put("CUST_ID",custId);
        map.put("MERCHANT_ID",merchanId);

        //断言(包含响应报文断言和数据库断言)
        String xmlFileName = this.getClass().getSimpleName(); //数据库断言xml文件名(与类名保持一致)
        asserts(caseMess, bodyString, response.asString(),xmlFileName,map);
    }
}
4、用例集

对于多个suite,可通过suite-files配置。testng.xml文件配置如下。





    
        
        
    

    

        
            
            
        
    

5、Jenkins构建

选择环境及测试用例集,开始构建,构建完成后生成测试报告及日志。也可根据需要设置定时构建,持续进行质量监控。 Jenkins构建

Jenkins构建后

Jenkins构建

6、报告分析

在这个注重颜值的世界,allure框架出来的测试报告绝对称得上“报告界的小鲜肉”。

测试报告总览包含用例通过率、测试套件、环境、feature、类别、趋势等信息。以下示例截图的案例全部执行失败,所以总览的通过率是0%。 测试报告

类别主要展现失败的用例信息,可根据项目情况自定制报告内容,比如请求报文、响应报文、断言结果等。 测试报告

测试报告

时间刻度展现了每条案例的执行时间。 测试报告

五、框架实现方案 1、工具/框架
  • Jenkins
  • Maven
  • Gitlab
  • TestNG
  • Rest Assured
  • Allure
2、工程目录

工程目录

工程目录

3、pom依赖

支持多环境(sit,uat)切换,结合Jenkins使用。



    4.0.0

    HFIIACCOUNT
    ApiAutoTest
    1.0-SNAPSHOT

    
    
        1.8.10
        
        UTF-8

        
        
    

    
        
            org.testng
            testng
            6.11
        

        
            io.rest-assured
            rest-assured
            3.1.0
        

        
            ru.yandex.qatools.allure
            allure-testng-adaptor
            1.3.6
            
                
                    org.testng
                    testng
                
            
        

        
            io.qameta.allure
            allure-testng
            2.0-BETA14
        

        
            net.sourceforge.jexcelapi
            jxl
            2.6.12
        

        
            com.google.code.gson
            gson
            2.8.2
        

        
            com.alibaba
            fastjson
            1.2.13
        

        
            com.oracle
            ojdbc14
            10.2.0.4.0
        

    

    
        
            
                src/main/resources
                true
            
        

        
            src/main/filters/filter_${env}.properties
        

        
            
                org.apache.maven.plugins
                maven-surefire-plugin
                2.20
                
                    
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    
                    
                    
                        
                            allure.results.directory
                            ./target/allure-results
                        
                    
                
                
                    
                        org.aspectj
                        aspectjweaver
                        ${aspectj.version}
                    
                
            

            
                org.apache.maven.plugins
                maven-surefire-plugin
                2.19
                
                    
                        
                        src/main/resources/testngXml/${xmlFileName}
                        ${project.basedir}/target/classes/testngXml/${xmlFileName}
                    

                
            

            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    8
                    8
                
            

            
            
            
            
                org.apache.maven.plugins
                maven-resources-plugin
                2.6
                
                    
                        
                        xls
                    
                
            

        
    

    
    
        
        
            uat
            
                uat
            

        

        
        
            sit
            
                sit
            
            
                true
            
        

    


4、实现思路
  • 将每个接口的案例读取到Map保存,其中key值为每条案例的caseNo+testPoint+preResult+YorN+tableCheck拼装(也是一个map),value值为剩余字段(接口入参)的拼装,即接口请求的bodyString。

案例模板

public static void getMap(Sheet sheet, int cols, int row, String pubArgs){

        for (int col = 0; col = 5) {
                //appid,api,version属于公共入参,公共入参字段在PublicArgs.properties文件进行配置
                // getBuildValue(value1,value2)方法用于转换${}或者函数为对应的值
                if (pubArgs.toLowerCase().contains(cellKey.toLowerCase().trim())) {
                    bodyMap.put(cellKey, RespondAssertForJson.getBuildValue("", sheet.getCell(col, row).getContents()));
                } else {
                    dataMap.put(cellKey, RespondAssertForJson.getBuildValue("", sheet.getCell(col, row).getContents()));
                }
            } else {
                caseMessMap.put(cellKey, cellValue);
            }
        }
        bodyMap.put("data", dataMap);
        map.put(new Gson().toJson(caseMessMap), new Gson().toJson(bodyMap));
    }
  • 对于案例中的自定义函数或供数变量,封装了方法RespondAssertForJson.getBuildValue(var1,var2)进行转换,目前暂支持前文提到的五种供数方式,后续可根据需要进行扩展。
    /**
     * 支持json串转换
     * 支持自定义函数的转换
     * 支持${}变量转换
     *
     * @param sourchJson
     * @param key
     * @return
     */
    public static String getBuildValue(String sourchJson, String key) {
        key = key.trim();
        Matcher funMatch = funPattern.matcher(key);
        Matcher replacePattern = replaceParamPattern.matcher(key);

        log.info("key is:" + key);
        try{
            if (key.startsWith("$.")) {// jsonpath
                key = JSONPath.read(sourchJson, key).toString();  //jsonpath读取对应的值
                log.info("key start with $.,value is:" + key);
            } else if (funMatch.find()) {//函数

                String args = funMatch.group(2);  //函数入参
                log.info("key is a function,args is:" + args);
                String[] argArr = args.split(",");
                for (int index = 0; index             
关注
打赏
1663571372
查看更多评论
0.1030s