利用MeterSphere实现高级断言实践

测试背景描述

由于金融系统要求的严肃性,我司要求在接口测试过程中,对于接口测试的数据,无论是查询数据还是写入数据,都必须从数据库中进行查询后比对校验。在以往的接口测试过程中需要测试人员一方面进行接口测试的同时,另一方面需要及时查库人工比对,从而导致测试效率低,一直无法进行大规模的回归测试。在此之前也尝试过采用pytest进行接口的自动化回归测试,但是由于pytest对测试人员能力要求较高,而且对大量的接口测试用例难以形成有效管理,所以采用Pytest技术框架路线从也难以持续推进。

一次偶然机会得知MeterSphere项目https://www.fit2cloud.com/metersphere/index.html,MeterSphere不同于PostMan,Jmeter等工具,从平台角度对测试进行管理,盖测试跟踪、接口测试、性能测试等功能,还兼容Jmeter标准。所以我司在MeterSphere上尝试了下接口自动化场景测试。

测试场景描述

此系统测试要求接口返回数据与数据库查询结果的比对。需要将要比对的数据从接口返回结果和数据库查询结果中取出,然后再进行逐个字段值进行循环比较。由于循环断言比对具有一定的复杂性,针对不同的场景需要采用不同的断言方式,目前主要涉及到两种断言模式:BeanShell高级断言和ForEach循环断言。以用户注册场景为例:

此系统的注册涉及到注册人的身份证、手机号、用户名称、客户类型、银行卡号等数据,步骤为手机验证-》身份证验证-》设置密码-》注册成功登陆-》银行卡信息验证-》绑定成功,细节步骤如下:

  1. 手机验证:输入手机号-》发送验证码-》数据库查询验证码-》界面输入验证码-》校验是否注册过该手机号-》手机验证通过
  2. 身份证验证:上传身份证正反面-》识别身份证有效性-》维护个人信息-》验证通过
  3. 设置密码:设置密码-》是否满足密码要求-》通过
  4. 注册成功:注册成功-》进行登录
  5. 银行卡信息验证:输入银行卡号-》银行卡号识别-》发送验证码-》数据库查询验证码-》界面输入验证码-》校验验证码是否正确-》完成绑卡
  6. 银行卡绑定成功:绑定成功-》查询用户银行卡信息-》校验银行卡信息-》验证成功

该场景涉及6个步骤,涉及14个接口,断言14个,提参14个,自定义脚本8个。

整体测试逻辑为用户在接口请求信息需要从数据库进行一次查询,采用数据库查询结果和界面请求响应进行断言比对,比对通过后则测试通过。

  1. 测试前数据准备

因为测试涉及到人员信息、手机号码、身份证、银行卡等信息。此为敏感信息,所以需要提前伪造测试数据信息,在以往的测试模式中,都采用的python脚本提前生成测试数据,然后在人工复制至接口测试用例中,此工作量巨大。MeterSphere中支持MockJS,通过MockJS可以提前伪造号人员信息数据,身份证信息等。如下图伪造身份证号

利用MeterSphere实现高级断言实践

测试数据可以提前在项目管理的环境变量中进行定义或者接口自动化中的公共参数定义,后续在接口用例或者接口自动化场景中使用${Idcard}进行使用:

 

  1. Beanshell脚本断言

具体操作步骤如下:
一、MeterSphere设置数据库源,创建SQL请求,将查询结果按照“存储结果”进行存储,返回的数据示例为:

利用MeterSphere实现高级断言实践

二、创建接口请求,配置请求环境变量,获取接口返回的JSON数据

返回数据示例:

利用MeterSphere实现高级断言实践

在此接口下添加后置脚本,提取接口响应body数据至环境变量messgae中(也可以采用JsonPath进行提取),如下图:

利用MeterSphere实现高级断言实践

三、将数据库返回数据和接口数据作比对:利用beanShell断言

添加断言规则

利用MeterSphere实现高级断言实践

选择类型为脚本,并编写脚本保存

  1. 利用MeterSphere实现高级断言实践

 

具体的断言脚本如下:

     import  org.json.*;  //导入需要用到的包

     List share=vars.getObject("bankListInfo"); //获取数据库的存储结果

    String data = vars.get("message");   //获取接口数据

    JSONObject body = new JSONObject(data); //将接口数据转化为JSON对象

        String keyDesc = "rec";   //即将要断言数据对象

    if(!body.has(keyDesc) ){  //判断接口数据是否包含“rec”对象

        if(share == null || share.size() < 1){ //如果不包含断言对象、数据库返回为空,则断言失败

            AssertionResult.setFailure(true); //显示断言是否失败 true是失败,false为成功

            AssertionResult.setFailureMessage("rec信息与数据库返回均为空!!!");//显示断言信息

            return;

        }

        AssertionResult.setFailure(true);

        AssertionResult.setFailureMessage("rec信息不存在");

        return;

    }

    JSONArray rec = body.getJSONArray("rec");//上面已判断当前对象已存在

    if (rec == null || rec.length() < 1) { //上面已判断当前对象是否数据为空

        AssertionResult.setFailure(true);

        AssertionResult.setFailureMessage("rec信息为空");

        return;

    }

       if (share.size() != rec.length()) { //数据库查询结果与接口返回结果数量不一致,则断言失败

        AssertionResult.setFailure(true);

        AssertionResult.setFailureMessage("数据库查询结果与接口返回结果数量不一致,数据库数量:"+share.size()+" 接口返回数量为:"+rec.length());

        return;

    }

     String detail = "rec_Detail";//“rec”对象的子集

     //beanshell显示数据库的示例数据:  测试数据:[{name=xxxx, description=nnnnn, id=wwwww, create_time=1609149399861}]

    //下面则为判断接口返回数据内容和数据库返回内容相同字段的值是否一致

    for (Object datum : share) {  //这里的判断逻辑是以数据库的返回数据为准,循环遍历数据库的返回结果

        Map map=(Map)datum; //单行数据,是以key:value存储的

        for(Object k:map.keySet()){ //遍历每一个字段

            String key=k; //字段名称 key

            Object value=map.get(k); //字段值 value

            boolean recFlag=false; //标志位:是否断言成功

            String resultMessage = "";  //断言信息

            for (int i = 0; i < rec.length(); i++) {// 先判断rec下的所有字段是否匹配

                JSONObject dataObject = rec.getJSONObject(i);

                if(value!=null && dataObject.has(key) ){

                    String resultStr = dataObject.getString(key);

                    if((value.toString()).equals(resultStr) ){

                        recFlag=true;

                        break;

                    }

                    resultMessage = "数据库返回结果:" + value + "  JSON报文结果:" + resultStr;

                }

              

                if(!dataObject.has(detail)){//判断 "rec"是否有 子集

                 continue;

                }

                JSONArray detailData = dataObject.getJSONArray("rec_Detail");//获取子集信息

 

                if(detailData==null || detailData.length()<1){  //判断 "rec"的子集是否为空

                 continue;

                }

                for(int n=0;n<detailData.length();n++){ //如果不为空,则继续和数据库返回值作比对

                        JSONObject detailObject = detailData.getJSONObject(n);

                        if(value!=null && detailObject.has(key) ){

                            String resultStr1 = detailObject.getString(key);

                            if((value.toString()).equals(resultStr1) ){ //这里全部当成字符串比对

                                recFlag=true;

                                break;

                            }

                            resultMessage = "数据库返回结果:" + value + "  JSON报文结果:" + resultStr;

                        }

                }

            }

            if(!recFlag){ //设置比对结果

                AssertionResult.setFailure(true);

                AssertionResult.setFailureMessage("存在不匹配字段为:" + key + "---> " + resultMessage);

            }

        }

}

四:此场景运行结果如下:

利用MeterSphere实现高级断言实践

  1. ForEach循环断言
  2. ForEach控制器在Jmeter中属逻辑控制器其中的一种,可以根据多个变量依次被循环调用,直到最后一个变量被调用即结束循环。在ForEach循环里面将请求加入断言,可以实现循环断言的功能。

    具体操作步骤如下:

    一:对接口使用JsonPath使用多行匹配进行提取:

  3. 利用MeterSphere实现高级断言实践

  4. 提取的报文如下:

利用MeterSphere实现高级断言实践

二:使用ForEach循环对接口提取值进行循环SQL请求,并添加脚本断言:

利用MeterSphere实现高级断言实践

利用MeterSphere实现高级断言实践

ForEach循环下添加SQL请求

利用MeterSphere实现高级断言实践

SQL请求下添加脚本断言

三:此场景报告如下:

利用MeterSphere实现高级断言实践

 

上一篇:拓扑排序


下一篇:CCF CSP 201409-2 画图 (C++)