51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 5468|回复: 5
打印 上一主题 下一主题

[原创] Postman 做接口自动化测试-2. 常用代码片段

[复制链接]
  • TA的每日心情

    2016-10-29 22:53
  • 签到天数: 18 天

    连续签到: 1 天

    [LV.4]测试营长

    跳转到指定楼层
    1#
    发表于 2017-5-16 16:11:55 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
    如果不打算用来做自动化测试,以下绝大部分都没必要

    对随便在界面发下请求来说太麻烦

    Tests常用函数

    假设接口返回JSON

    前提:globals里存了常用的函数,如:

    assertNotTimeout:var hasResponse=postman.getResponseHeader('Content-Type')?true:false; if(!hasResponse) tests['服务端在超时前没返回任何数据,请检查相关服务、网络或反向代理设置(以下跳过其他断言)']=false;
    logParams:if(hasResponse) tests[`[INFO] 请求参数(仅限POST,超时没返回时不解析):${JSON.stringify(request.data)}`]=true;
    getResponseJson:try{if(hasResponse) var json=JSON.parse(responseBody);}catch(err){ tests['服务端没返回合法的JSON格式,请检查相关服务、网络或反向代理设置(以下跳过其他断言)']=false; tests[`[INFO] 返回:${responseBody}`]=true; console.error(err);}
    assertType:var assertType=(name,value,type)=>{let isType=(type==='array')? Array.isArray(value):typeof value===type; tests[`${name}为${type}(实际值:${value})`]=isType;};
    assertEqual:var assertEqual=(name,actual,expected)=>{tests[`${name}等于${expected}(实际值:${actual})`]=actual===expected;};
    assertNotEqual:var assertNotEqual=(name,actual,expected)=>{tests[`${name}不等于${expected}(实际值:${actual})`]=actual!==expected;};
    这块难看不是太大问题,1个人写好、导出、提交Git,通知其他人拉、导入,完事

    然后就可以一种套路走天下——

    开头把函数全拿出来:

    // setup
    eval(globals.assertNotTimeout);  // 判断是否超时
    eval(globals.logParams);  // 在报告里显示POST请求参数
    eval(globals.getResponseJson);  // 返回json变量,保存了整个返回的JSON对象
    // 定义几个常用函数,参数都是 显示文字、实际值、期望值
    eval(globals.assertType);
    eval(globals.assertEqual);
    eval(globals.assertNotEqual);
    假设项目里每个接口必定会带resultCode属性,成功返回1,

    失败时还会带resultMsg detailMsg属性,但未必2个都有提示信息

    先来个通用的套路,不管接下来漏什么,总不会把最基本的漏了:

    // 项目通用的断言
    if (json) {
      const { resultCode, resultMsg, detailMsg } = json;
      assertEqual('resultCode', resultCode, 1);

      if (resultMsg) tests[`[INFO] 接口提示信息:${resultMsg}`] = true;
      if (detailMsg) tests[`[INFO] 接口提示信息:${detailMsg}`] = true;
    }
    如果这接口报错也是正常情况(如检查手机号是否已注册的接口,毕竟随机生成的号码有可能跟已有的重复),加上流程控制,反复通过名字调自己,直到得到想要的结果

    if (!resultCode || resultCode !== 1) {
      tests[`[WARN] 手机号${environment.randomMobile}无法注册,重试中……`] = true;
      postman.setNextRequest('验证手机是否注册');
    }
    如果这接口执行失败会导致之后的接口没有跑的意义,那就中止测试流程

    (比如注册失败,接下来需要登录的操作都不用测了)

    // (注明中止理由)
    if (!resultCode || resultCode !== 1) {
      tests['[ERROR] 执行失败,跳过依赖本接口的后续测试'] = false;
      postman.setNextRequest(null);
    }
    在界面/HTML报告里看到的assertEqual输出例:resultCode等于1(实际值:1)

    有些接口返回内容很简单,不需要做什么验证,到上面就结束了

    门槛低到只要会复制粘贴就“能做自动化测试”(shell脚本别人写,Jenkins别人配),还很稳定!

    当然,全都像上面这样搞的话会有很多假阴性

    很多接口还要断言更多东西

    如果返回值需要和环境变量/请求参数/固定的值做对比,与其写得到处都是,将来改起来忘了这忘了那

    不如都塞一个对象里,要改一次改完:

    // 该接口的断言
    const expected = {
      mobile: environment.PATIENT_MOBILE,
      userType: 1,
    };

    // 如果值来自写死的请求参数:request.data.变量名
    // 如果值来自数据文件:data.变量名
    // Postman还是很体贴的,给了你各种实用的全局对象(虽然在文档里藏得很深,虽然那个data极易重名……)
    假设项目里大多数接口返回的JSON里都有个data对象,各种业务相关属性都在里面

    接下来又一个套路:

    if (json && json.data) {
      // ...
    } else {
      tests['返回值包含data对象'] = false;
    }
    if里断言什么提取什么就跟具体接口有关了,需要了解业务逻辑、查接口文档、问开发等

    但还是有不少套路

    首先总要把某些属性取出来放进变量吧(这里用了ES6的对象解构)

    const { access_token, patientId, userId, login, user } = json.data;
    需要放进环境变量传给下个请求用的东西,总得做点断言吧,就算没法断言具体的值,判断类型还是可以的

    assertType('令牌', access_token, 'string');
    assertType('患者ID', patientId, 'number');
    assertType('user', user, 'object');

    environment.patientId = patientId;
    environment.PATIENT_ACCESS_TOKEN = access_token;
    返回的某个对象里如果需要做更精细的断言,继续拆

    上面我们定义的expected对象在这里用上了

    if (user) {
      const { telephone, userId, userType, } = user;
      assertEqual('mobile', telephone, expected.mobile);
      assertEqual('userId', userId, json.data.userId);
      assertEqual('userType', userType, expected.userType);
    }
    PS:

    专门用难维护的globals定义函数的意义就在这里,断言数量太多了(上面省略了一些)

    如果用官方的写法,想想满屏差不多又有点不同的东西维护起来多恐怖……

    tests[`foo为string类型(实际值:${foo})`] = typeof foo === 'string';
    tests[`bar等于1(实际值:${bar})`] = bar === expected;
    【总结】

    写断言的套路不止1个,但个人认为这个比较适合小公司/小项目(最底下还有个JSON Schema的)

    上面的代码块连起来搞成一个可以复制粘贴的模板就能到处用

    等以后新版本出来了,支持在集合/文件夹级别定义函数,可能就没必要到处复制粘贴,甚至不需要globals了

    Pre-Request Script常用函数

    当前时间戳

    有些接口,如拉消息,返回比提交的时间戳新的数据

    // 如果不打算重用,在参数里用Postman的内建变量`{{$timestamp}}`就行,否则:

    environment.ts = Date.now();
    如果本地/测试服务器和应用服务器时间不同步会影响结果,可以考虑加减一定毫秒数
    对时间非常敏感的接口可能不适合用自动化的手段验证,要仔细选取用例/场景,不能为做而做
    UUID

    // 如果不打算重用,在参数里用Postman的内建变量`{{$guid}}`就行,否则:

    const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
      .replace(/x/g, () => (Math.floor(Math.random() * 16)).toString(16))
      .replace(/y/g, () => (Math.floor(Math.random() * 4 + 8)).toString(16));
    这写法比较易懂,在现在的机器上跑只需要0-1ms,足够了
    更多讨论见 stackoverflow的帖子
    随机

    让请求参数有点变化

    // 如果想要0~1000的随机数,且不打算重用,参数里直接用Postman内建变量`{{$randomInt}}`就行
    // 否则自己实现:

    const randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;  // 随机整数
    const getRandomValue = list => list[randomInt(0, list.length - 1)];  // 随机选项
    例:

    // 随机手机
    environment.randomMobile = `18${randomInt(100000000, 999999999)}`;
    // 随机2-6字姓名
    const charsInName = ['赵', '钱', '孙', '李', '王', '张'];
    const numOfChars = randomInt(2, 6);
    let randomName = '';
    for (let i = 0; i < numOfChars; i++) {
      let index = randomInt(0, 5);
      randomName += charsInName[index];
    }
    environment.randomName = randomName;
    // 随机设备token(推送服务商提供)
    const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
    let deviceToken = '';
    for (let i = 0; i < 64; i++) {
      deviceToken += getRandomValue(chars);
    }
    environment.randomDeviceToken = deviceToken;
    // 随机设备名
    environment.randomDevice = getRandomValue(['ios', 'android']);
    // 随机行政区划
    const divisions = ['北京市', '上海市', '天津市', '重庆市', '广东省 深圳市', '广东省 广州市', '新疆维吾尔自治区 克孜勒苏柯尔克孜自治州'];
    environment.randomDivision = getRandomValue(divisions);
    // 随机生日(时间戳)
    // 假设今天是2017-1-1,距1970-1-1 47年,则生日范围为 1923-1-1 ~ 2017-1-1
    environment.randomBirthday = randomInt(0 - Date.now(), Date.now());
    // 随机群名
    const groupNames = ['犯罪团伙', 'We are gay', '`~!@#$%^&*()-_ =+'];
    environment.groupName = getRandomValue(groupNames) + randomInt(0, 1000);
    环境变量未定义就赋初始值

    environment.XXX == null || environment.NAME = value;
    // == null 匹配 null 和 undefined
    // 通常避免 !environment.XXX 或 environment.XXX || ... 的写法,变量有可能是false, '', 0
    同步等待

    避免发送请求的速度比数据库更新速度快,造成误报

    const sleep = (milliseconds) => {
    const start = Date.now();
    while (Date.now() <= start + milliseconds) {}
    };

    // 就是限时的死循环,请用小一点的数字调试
    // Postman是用JS写的,单线程异步,主线程被阻塞了就没法做其他操作
    修改密码

    用于修改密码接口,2套密码来回替换

    // 假设已设置了环境变量PWD

    const oldPwd = environment.PWD;
    const newPwd = environment.NEW_PWD;

    if (!newPwd || newPwd === oldPwd) {
        newPwd = '123456';
    } else {
      const tmp = oldPwd;
      oldPwd = newPwd;
      newPwd = tmp;
    }

    environment.NEW_PWD = newPwd;
    environment.PWD = oldPwd;
    (备忘)用JSON Schema校验格式

    仅仅因为Postman支持tv4,试着在项目用了下,效果不好

    哪来那么多时间写(这个看项目)
    太长了,在Postman里拉几屏看不全,不好维护
    通用的不通用的断言和异常处理都在一块,改起来容易漏
    别人一看就吓跑,拉不到人入坑
    被测接口如果功能不稳定,需求不明确,经常改的话,维护工作量太大
    // 依然假设返回的JSON里有个字段叫resultCode,1表示成功
    // (当时还没想到用globals存函数,中间反序列化JSON那段基本上就是现在的globals.getResponseJson)

    const schema = {
      // 这里手写/贴上在线工具生成的一长串JSON schema
    }

    let json;
    try {
      json = JSON.parse(responseBody);
    } catch(err) {
      tests['服务端没返回合法的JSON格式,请检查相关服务、网络或反向代理设置(以下跳过其他断言)'] = false;
      tests[`[INFO] 返回:${responseBody}`] = true;
      console.error(err);
    }

    if (json) {
      const result = tv4.validateResult(json, schema);
      tests['JSON Schema格式正确'] = result.valid;

      if (result.valid) {
        tests.isSuccess = json.resultCode === 1;

        if (tests.isSuccess) {
          // ...
        }
      } else {
        console.error(result.error);
        console.error(responseBody);
      }
    }
    JSON Schema可用 这网站 生成,把返回的JSON字符串贴进去,点Generate Schema(通常默认选项就够用了)
    注意不要复制"$schema":那行,Postman不支持引用外部模板
    按默认参数,会把贴进去的JSON里的所有字段都认为是必须的。如果某些返回字段是可选的,找到相应的"required":数组,去掉那字段
    Tiny Validator

    JSON Schema

    评分

    参与人数 1测试积点 +10 收起 理由
    lsekfe + 10 赞一个!

    查看全部评分

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏2
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2015-10-17 08:41
  • 签到天数: 22 天

    连续签到: 1 天

    [LV.4]测试营长

    4#
    发表于 2018-1-2 00:19:41 | 只看该作者
    好厉害,借鉴借鉴
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2019-12-31 08:59
  • 签到天数: 975 天

    连续签到: 1 天

    [LV.10]测试总司令

    3#
    发表于 2017-5-17 09:02:18 | 只看该作者
    支持分享,postman 用来调试不错
    回复 支持 反对

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-14 10:27 , Processed in 0.074890 second(s), 26 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表