2020-05-12Api测试用例参数化

2020-05-12  本文已影响0人  testerPM

问题: 为什么要做参数化?

1.以注册接口的测试用例为例: 接口参数mobile_phone目前在excel中是写死的,执行脚本测试该接口,下一次如果再重新执行,就要重新改excel中的mobile_phone以及pwd,我们不想每次都手动修改excel,如果能在程序中每次执行脚本都可以自动获取到一个随机的mobile_phone和pwd,这样每次重新执行脚本测试接口,就能保证每个测试用例都能被执行


image.png
  1. 下图是充值接口,member_id也是写死的,是从数据库中查到这个登陆用户的member_id,如果将member_id进行参数化,直接读进来,这样程序脚本是不是更灵活呢?


    image.png

如何进行参数化呢?

1.以注册用例
手机号和密码作为参数化获取,不过这里,我们一般对传入正确手机号,密码进行参数化,错误的手机号,密码不用进行参数化,因为只有正确的数据,下次执行脚本才会有影响(如:手机号被注册过了,就无法注册成功了,那么注册成功的case就不能被执行)


image.png

检验sql 也要替换成参数化


image.png

2.充值接口

image.png
image.png

如何进行参数化替换?

如何把 {toBeRegisterMobilephone},{toBeRegisterPassword}替换成具体的值呢?

//获取接口参数
cases.getParams();
    //获取到接口参数如下 
{"mobile_phone":"${toBeRegisterMobilephone}","pwd":"${toBeRegisterPassword}"}
//问题: 怎么把上面的json 转换下面的json呢???
        {"mobile_phone":"13333333333","pwd":"222222"}


解决办法如下:加到常量类,常量类新增如下代码:

public class Constant {
    //接口参数字段
    public  static final String  REGISTER_MOBILEPHONE_TEXT="${toBeRegisterMobilephone}";
    public  static final String  REGISTER_PASSWORD_TEXT="${toBeRegisterPassword}";
    public  static final String  LOGIN_MOBILEPHONE_TEXT="${toBeLoginMobilephone}";
    public  static final String  LOGIN_PASSWORD_TEXT="${toBeLoginPassword}";
    public  static final String  MEMEBER_ID="${member_id}";
    //接口参数值
    public  static final String  REGISTER_MOBILEPHONE_VALUE="18999999999";
    public  static final String  REGISTER_PASSWORD_VALUE="123456";
    public  static final String  LOGIN_MOBILEPHONE_VALUE="18999999999";
    public  static final String  LOGIN_PASSWORD_VALUE="123456";
    



}


参数替换字段和参数值如何发生关联呢???

即:REGISTER_MOBILEPHONE_TEXT和REGISTER_MOBILEPHONE_VALUE如何关联起来呢???

解决办法:使用map

(1)AuthenticationUtils.java类中已定义好一个个map
public class AuthenticationUtils {
    // 静态变量
    public static Map<String, String> env = new HashMap<String, String>();
}
(2)将参数放进map

以登录为例:LoginCase.java类加入以下新增代码

    @BeforeSuite
    public  void  init() {
        //套件执行初始化:配置参数化变量
        AuthenticationUtils.env.put(Constant.REGISTER_MOBILEPHONE_TEXT,Constant.REGISTER_MOBILEPHONE_VALUE);
        AuthenticationUtils.env.put(Constant.REGISTER_PASSWORD_TEXT,Constant.REGISTER_PASSWORD_VALUE);
        AuthenticationUtils.env.put(Constant.LOGIN_MOBILEPHONE_TEXT,Constant.LOGIN_MOBILEPHONE_VALUE);
        AuthenticationUtils.env.put(Constant.LOGIN_PASSWORD_TEXT,Constant.LOGIN_PASSWORD_VALUE);
    
    
    }
    


member_id是不能通过初始化加进去,因为member_id是需要通过动态获取的(JSONPath从响应体中获取)
AuthenticatonsUtils.java新增代码

// 2.获取member_id
            Object memberId = JSONPath.read(responseBody, "$.data.id");
            if (memberId != null) {
                env.put("memberId", memberId.toString());

            }


public class AuthenticationUtils {
    // 静态变量
    public static Map<String, String> env = new HashMap<String, String>();

    public static void storeToken(String responseBody) {
        // 1 取出token---jsonpath提供的语法格式取
        // $代表从根元素开始找,一层层找下去
        Object token = JSONPath.read(responseBody, "$.data.token_info.token");// 如果找不到返回的是null

        // 2 存储token
        if (token != null) {
            env.put("token", token.toString());// token是Object类型,对象转字符串才可以存进去,因为env Map<String,String> 是String
            // 2.获取member_id
            Object memberId = JSONPath.read(responseBody, "$.data.id");
            if (memberId != null) {
                env.put("memberId", memberId.toString());

            }

        }
    }
}


(3)循环遍历key:

map遍历---》这里是把key放到set集合,然后增强for循环进行遍历
一共就有五个key,逐一判断params中是否有这五个key,params中只要有key就替换成value

String params = cases.getParams();// 获取第1个,第2个,第3个......用例接口参数(用例一个个读进来)

        Set<String> keySet = AuthenticationUtils.env.keySet();// 获取key集合
        //为何要进行遍历:这样做就不用知道params中使用到了哪些参数变量,当读取第一个用例时,通过遍历所有的key,第一个用例params中只要有key就替换成value
        for (String key : keySet) {
            String keyValue = AuthenticationUtils.env.get(key);// 获取key的值
            params.replace(key, keyValue);// params是否包含key,如果有key,就将其替换成keyValue,没有就不替换
            // 遍历完所有的key会自动跳出循环
        }

上面代码抽取优化到BaseCase.java类中:新增代码如下

    /**
     * 参数化变量替换
     * @param params
     * @return
     */
    public String parmasReplace(String params) {

        Set<String> keySet = AuthenticationUtils.env.keySet();// 获取key集合
        // 为何要进行遍历:这样做就不用知道params中使用到了哪些参数变量,通过遍历所有的key(一共就有四个key,逐一判断params中是否有key,params中只要有key就替换成value)
        for (String key : keySet) {
            String keyValue = AuthenticationUtils.env.get(key);// 获取key的值
            params.replace(key, keyValue);// params是否包含key,如果有key,就将其替换成keyValue,没有就不替换
            // 遍历完所有的key会自动跳出循环
        }
        return params;
    }


LoginCase.java类进行调用上述方法即可:


image.png

到此代码就写完了,执行LoginCase.java


image.png

从上图可以看到有两个错误,第一:参数没有替换成功, 第二,报了空指针

(1)先看第一个错误,为什么没有被替换成功
图1:可以看到所有的key,按f6执行下一步到图2


image.png

图2:可以看到key对应的value就是图3中的值


image.png

图3:按f6执行到下一步 图4


image.png

图4: 下图key的values是图5


image.png
图5:按f6执行下一步遍历结束后执行 到图6
image.png

图6:下图可以看到,返回值没有被替换

image.png

原因定位:params = params.replace(key, keyValue);需用变量接收替换后的值

image.png

(2)为什么会报空指针

image.png

上图报错,第一行提示报错出在 BaseCase.java:62行,点击此处会自动跳到报错的地方


image.png

分析第62行代码,key,keyValued都有值,不会为空,那么问题就出在传进来参数params可能为空,空对象调方法一般会报空指针,再看报错第二行,可以看到params是哪里传进来的


image.png

上图可以分析得到:传入的sql为空,所以需要优化代码:BaseCase.java类优化参数替换方法parmasReplace()

    /**                                                                                                                                              
     * 参数化变量替换                                                                                                                                       
     *                                                                                                                                               
     * @param params                                                                                                                                 
     * @return                                                                                                                                       
     */                                                                                                                                              
    public String parmasReplace(String params) {            
//判断params为空的处理                                                                                         
        if (StringUtils.isBlank(params)) {                                                                                                           
            return "";                                                                                                                               
        }                                                                                                                                            
                                                                                                                                                     
        Set<String> keySet = AuthenticationUtils.env.keySet();// 获取key集合                                                                             
        // 为何要进行遍历:这样做就不用知道params中使用到了哪些参数变量,通过遍历所有的key(一共就有四个key,逐一判断params中是否有key,params中只要有key就替换成value)                                          
        for (String key : keySet) {                                                                                                                  
            String keyValue = AuthenticationUtils.env.get(key);// 获取key的值                                                                            
                                                                                                                                                     
            params = params.replace(key, keyValue);// Primes是否包含key,如果有key,就将其替换成keyValue,没有就不替换                                                     
            // 遍历完所有的key会自动跳出循环                                                                                                                      
        }                                                                                                                                            
        return params;                                                                                                                               
    }                                                                                                                                                

备注:

(1)注册和充值接口参数替换代码自行补充(只需要在各自的类进行调用替换方法即可),即:将下面代码分别copy到RegisterCase.java,RechargeCase.java中即可

    //接口参数变量替换
        String params = parmasReplace(cases.getParams());
        System.out.println("替换后的params:" + params);
        //将替换后的params塞到cases对象里
        cases.setParams(params);
        // sql参数替换
        String SQLparams = parmasReplace(cases.getSql());
        System.out.println("替换后的SQLparams :" + SQLparams);
        //替换的后的sql塞到cases对像里面
        cases.setSql(SQLparams);


(2)如果需要单独运行注册,充值接口,则RegisterCase.java,RechargeCase.java各自都要加上下面代码:

@BeforeSuite
    public void init() {
        // 套件执行初始化:配置参数化变量
        AuthenticationUtils.env.put(Constant.REGISTER_MOBILEPHONE_TEXT, Constant.REGISTER_MOBILEPHONE_VALUE);
        AuthenticationUtils.env.put(Constant.REGISTER_PASSWORD_TEXT, Constant.REGISTER_PASSWORD_VALUE);
        AuthenticationUtils.env.put(Constant.LOGIN_MOBILEPHONE_TEXT, Constant.LOGIN_MOBILEPHONE_VALUE);
        AuthenticationUtils.env.put(Constant.LOGIN_PASSWORD_TEXT, Constant.LOGIN_PASSWORD_VALUE);

    }

如果通过执行testNG.xml运行,则RegisterCase.java,RechargeCase.java各自就不需要要加上上面的代码(只要有一个类中有上面的代码即可,因为上面代码是在套件执行之前会将变量加到map里面)

疑问:执行充值接口为什么${member_id}没有被替换掉呢?

原因定位:(1)通过打断点,发现map集合key没有MEMBER_ID
这是为什么呢?-》因为:MEMBER_ID是需要通过登陆才能获取

Object memberId = JSONPath.read(responseBody, "$.data.id");
            if (memberId != null) {
        
                env.put("memberId", memberId.toString());

}

所以不能单独运行充值接口,需要依赖登录接口,此时运行通过运行testNG.xml,控制台打印结果发现,${member_id}还是没有被替换

通过打断点,发现keySet集合还是没有MEMBER_ID
原因:

   public  static final String  MEMBER_ID="${member_id}";
错误的写法:
Object memberId = JSONPath.read(responseBody, "$.data.id");
            if (memberId != null) {
                //env.put("memberId", memberId.toString());
                            // env.put("MEMBER_ID", memberId.toString());
                        
}

正确的写法:
Object memberId = JSONPath.read(responseBody, "$.data.id");
            if (memberId != null) {
        env.put(Constant.MEMBER_ID, memberId.toString());
}



再次打断点,发现keySet集合有MEMBER_ID(注意:只有member_id不为空才能put进去)


image.png

重新执行testNG.xml。控制台打印结果中$ {member_id}替换成功

待解决问题:为什么下面的写法语法没有报错呢

  // env.put("MEMBER_ID", memberId.toString());

java 随机生成手机号码(自己百度)



// 接口参数
    public static final String REGISTER_MOBILEPHONE_TEXT = "${toBeRegisterMobilephone}";
    public static final String REGISTER_PASSWORD_TEXT = "${toBeRegisterPassword}";
    public static final String LOGIN_MOBILEPHONE_TEXT = "${toBeLoginMobilephone}";
    public static final String LOGIN_PASSWORD_TEXT = "${toBeLoginPassword}";
    public static final String MEMBER_ID = "${member_id}";
    // 接口参数值
    public static final String REGISTER_MOBILEPHONE_VALUE = getTel();
    public static final String REGISTER_PASSWORD_VALUE = "123456789";
    public static final String LOGIN_MOBILEPHONE_VALUE = REGISTER_MOBILEPHONE_VALUE;
    public static final String LOGIN_PASSWORD_VALUE = "123456789";

    public static int getNum(int start, int end) {
        return (int) (Math.random() * (end - start + 1) + start);

    }

//  public static String[] telFirst = "134,135,136,137,138,139,150,151,152,153,154,155,156,157,158,159,130,131,132,133"
//          .split(",");

    private static String getTel() {

        String[] telFirst = "134,135,136,137,138,139,150,151,152,153,154,155,156,157,158,159,130,131,132,133"
                .split(",");
             // 打印出数组
        System.out.println(Arrays.toString(telFirst));
        int index = getNum(0, telFirst.length - 1);
        String first = telFirst[index];
        String second = String.valueOf(getNum(1, 888) + 10000).substring(1);
        String third = String.valueOf(getNum(1, 9100) + 10000).substring(1);
        return first + second + third;
    }

这里有个疑问,为什么定义一个静态全局变量数组【上面被注释掉的代码】,数组是NUll??? 但是放在getTel()方法体方是可以的,这里是为什么呢????
原因如下:
public static final String REGISTER_MOBILEPHONE_VALUE = getTel();这句代码在定义静态数组之前,且这里有调用getTel(),且getTel()方法体有使用到telFirst,而此时程序还没有执行到定义静态数组的语句即还没有被加载进来,所以会报null.

解决办法:把静态数据声明放在最前面,如下图

package com.lemon.constant;

import java.util.Arrays;

public class Constant {
    public static String[] telFirst = "134,135,136,137,138,139,150,151,152,153,154,155,156,157,158,159,130,131,132,133"
            .split(",");
    public static final String EXCEL_PATH = "src/test/resources/case_v6.xlsx";
    public static final int ACTUAL_RESPONSE_DATA_CELLNUM = 5;
    // 接口参数
    public static final String REGISTER_MOBILEPHONE_TEXT = "${toBeRegisterMobilephone}";
    public static final String REGISTER_PASSWORD_TEXT = "${toBeRegisterPassword}";
    public static final String LOGIN_MOBILEPHONE_TEXT = "${toBeLoginMobilephone}";
    public static final String LOGIN_PASSWORD_TEXT = "${toBeLoginPassword}";
    public static final String MEMBER_ID = "${member_id}";
    // 接口参数值
    public static final String REGISTER_PASSWORD_VALUE = "123456789";
    public static final String REGISTER_MOBILEPHONE_VALUE = getTel();
    public static final String LOGIN_MOBILEPHONE_VALUE = REGISTER_MOBILEPHONE_VALUE;
    public static final String LOGIN_PASSWORD_VALUE = "123456789";

    public static int getNum(int start, int end) {
        return (int) (Math.random() * (end - start + 1) + start);

    }

    private static String getTel() {
        int index = getNum(0, telFirst.length - 1);
        String first = telFirst[index];
        String second = String.valueOf(getNum(1, 888) + 10000).substring(1);
        String third = String.valueOf(getNum(1, 9100) + 10000).substring(1);
        return first + second + third;
    }

    public static void main(String[] args) {
        // System.out.println(getTel());
        System.out.println(Arrays.toString(telFirst));
    }

}


密码随机数(自行百度)

上一篇下一篇

猜你喜欢

热点阅读