zoukankan      html  css  js  c++  java
  • 接口自动化测试流程和相关准备工作

    第一步: 拿到需求文档、UI交互图(原型图)、数据库表设计文档、接口文档

    1问:为什么要拿到这些文档资料呢?

    1答:

    ①.《需求文档》,明确定义了:各个表单字段的限制条件;相关场景逻辑校验;

    ②.《UI交互图》,明确定义了:各单页面需展示的数据;页面之间的交互;

    ③.《数据表设计文档》,结合UI图和需求文档,明确定义了:表字段规则、表N多N关系(一对一、一对多、多对多);

    ④.《接口文档》,结合需求文档和UI和数据表,明确定义了:接口名,各个入参值,各个返回值,和其他相关信息;

    2细节:

    ①.大多数公司,针对这四类文档资料都整理的不规范,或者没能及时更新;

    ②.这会导致接口测试用例的编写没有一个绝对可靠的需求来源

    ③.因为,接口测试用例本质是针对各个表字段的校验;

    ④.所以需求文档里对各个表单字段的限制条件,产品人员务必都要写清楚,不要遗漏限制条件;

    ⑤.而接口文档里针对各个用例场景的返回值,文档里务必都要及时填写和更新;

    ⑥.针对接口文档返回值的字段:比如code=0表示登录成功,code=1表示密码错误;code=2表示无网络;code=3表示账号错误,等等类似code值有不同含义;

    3注意:

    ①.所以,在写接口测试用例之前,务必要先核对需求文档和接口文档,以最正确的需求文档和接口文档,来编写接口用例,才能得到最正确的结果;

    ②.先实现单接口解耦;后续按照业务场景组合多个接口;

    第二步: 拿到个人编写的接口用例模板,针对特定单接口编写接口用例

    1重点:要及时更新接口用例模板.xlsx,保证用例模板的准确性;

    2假设有一个后台系统banner广告位子模块,以特定单接口-createBanner-新增banner,来讲解编写单接口的接口用例的操作流程:

    1)新建一个xlsx后缀的excel文档,excel名改为:banner.xlsx

    2)打开banner.xlsx,第一个sheet页名称改为:创建banner

    3)打开接口用例模板.xlsx,把名称为【接口测试demo】的sheet页里的所有内容复制到banner.xlsx的名称为【创建banner】的空sheet页;

    4)在名称为【创建banner】的sheet页里,做如下步骤的操作:

    4-1)针对第一行各单元格数据(第一行各单元格是用来填写相关参数名和用于标识的参数名):

    按照实际需求来灵活配置:操作入参名所在的单元格;操作返回参数名所在的单元格;

    入参名都用红色字体表示;返回参数名都用绿色字体表示;

    比如:修改某个入参名,删除多余的一个入参名所在的一个列,新增一个列来填写一个新的入参名;

    比如:修改某个返回参数名,删除多余的一个返回参数名所在的一个列,新增一个列来填写一个新的返回参数名;

    ②跟入参名跟返回参数名所在的单元格无关的单元格,都保持原位置和原单元格名称即可。

    当然也支持任意移动位置和更改单元格名,但不建议更改单元格名因为更改了单元格名后代码也要跟着变动)

    4-2)针对第二行各单元格数据(第二行各单元格是用来填写默认值):

    ①单元格【验证字段】的值:不填,为空;

    ②单元格【接口地址】的默认值值是接口相对路径,结合实际情况比如更改为:/api/b.banner/creBanner,用于拼接该接口请求的绝对路径;

    ③单元格【请求方式】的默认值值是请求方式,结合实际情况比如更改为:post;

    ④单元格【编号】的值:不填,为空;

    ⑤单元格【返回值和断言结果的所在行下标】的值:不填,为空;

    ⑥单元格【用例类型】的值:不填,为空;

    ⑦单元格【用例目的】的值:不填,为空;

    ⑧单元格【用例名】的值:不填,为空;

    ⑨单元格【返回值】的值:不填,为空;

    ⑩单元格【断言结果】的值:不填,为空;

    细节1:如果入参名所在的单元格和返回参数名所在的单元格,单元格值可以重复且出现频率高,那就可以填写默认值

    细节2:如果入参名所在的单元格和返回参数名所在的单元格,单元格值不可以重复只能是唯一值,那就单元格值为空

    4-3)针对第三行及大于第三行的各单元格数据(用于填写具体值):

    结合接口用例模板.xlsx的名为【接口测试用例模板】sheet页称为A,在banner.xlsx的名为【创建banner】sheet页称为B,进行灵活填写;

    操作步骤:

    ①针对某个接口字段,复制A的三列数据【用例类型】【用例目的】【示范的用例名】,复制给B的三列数据【用例类型】【用例目的】【用例名】;

    ②然后删除不需要的用例名对应的行数据;

    ③更改各条剩下的用例名,比如更改字段名和相关数字等数据;(基本99%都要替换掉从模板复制过来的数据);

    ④B的这列数据【接口地址】,务必都填写DF;

    ⑤B的这列数据【请求方式】,务必都填写DF;

    ⑥B的这列数据【编号】,务必都填写文本形式的数字且数字递增,值从01开始(文本形式的数字可以再excel里设置);

    ⑦B的这列数据【返回值和断言结果的所在行下标】,务必都填写数字且数字递增,值从2开始;

    ⑧B的这列数据【验证字段】,灵活按照实际入参名来填写;

    ⑨B的两列数据【code】【data】都务必结合接口文档和需求文档,填写正确的值;(若哪些值觉得不合理,后期可以让产品或开发人员进行修改);

    ⑩B的两列数据【code】【msg】都务必结合接口文档和需求文档,填写正确的值;(若哪些值觉得不合理,后期可以让产品或开发人员进行修改);

    细节1:DF表示默认值,取值于第二行各自的单元格值(在脚本里有做相关转化和校验);

    细节2:&$,表示一个变量,由脚本内部赋值。比如: variable = {"${count}": 666} ;

    细节3:B的两列数据【返回值】【断言结果】都默认不填写,这两列数据都是由相关脚本返回的值;

    细节4: 相关颜色的标注,可结合实际来灵活填写;

    细节5: 列数据【data】和列数据【msg】不可能同时有值;

    细节6:待补充列表字段【预期值】,把【code】+【data】&【code】+【msg】这样组合为一个dict,回传写入【banner(包含接口返回值和断言结果).xlsx】;

    第三步: 结合项目框架,做相关流程的脚本操作

    1)第一,执行必须操作的步骤(按项目二级目录的排序顺序来执行)

    1. 在二级模块名【/data】内:编写脚本d_add_banner.py; (在子类D_add_banner的父类属性variable可结合banner.xlsx内的接口用例赋值情况,来重写该variable属性值;)

    2. 在二级模块名【/excel】内:存放banner.xlsx;

    3.在二级模块名【/expectedResult】内:编写脚本e_add_banner ; (第一次编写子类E_add_banner时,直接继承且不重写任何一个父类方法;调试期间,可按照实际重写父类相关方法;);

    4. 在二级模块名【/model】内:编写脚本m_add_banner.py; (第一次编写子类M_add_banner.py时,直接继承且不重写任何一个父类方法;调试期间,可按照实际重写父类相关方法;)

    5. 在二级模块名【/optimize】内:编写脚本o_add_banner.py; (第一次编写子类O_add_banner.py时,直接继承且不重写父类的sleep方法;调试期间,可按照实际重写父类的sleep方法;)

    6. 在二级模块名【/validate】内:编写脚本v_add_banner.py;(第一次编写子类V_add_banner.py时,直接继承且不重写父类的compareResult方法;调试期间,可按照实际重写父类的compareResult方法;)

    7. 在二级模块名【/writeCellValue】内:编写脚本w_add_banner.py;

    细节:各步骤对应的类都相对解耦,数据源基本都来自同个上游接口--d_add_banner.py内的子类D_add_banner所调用的父类方法excel_data()的返回值;

    2)第二,再执行非必须操作的步骤(按项目二级目录的排序顺序来执行)

    1. 在二级模块名【/fileAttribute】内:编写脚本f_add_banner.py;

    2. 在二级模块名【/public】内:存放图片视频等相关测试数据;

    3)第三,针对单接口createBanner,调试接口请求

    1. 在二级模块名【/controller】内:编写脚本c_add_banner.py; (第一次编写C_add_banner.py时,务必对某个特定的父类方法进行重写,比如:父类方法add,父类方法update;)

    细节1: 相关入参值都采取参数化;

    细节2:在【if __name__ == '__main__':】下方区域,进行单元测试的调试;

    细节3:用于调试的数据源data,可以在【d_add_banner.py内的子类D_add_banner所调用的父类方法excel_data()的返回值】这边获取,获取符合要求的其中一条数据来当做数据源;

    4)第四,针对单接口createBanner,结合ddt,遍历执行所有的接口测试用例

    1. 在二级模块名【/testcase】内:编写脚本test_001_add_banner_testcase.py;

    细节1:可直接复制其余现成的脚本,用ctrl+R快捷键统一替换相关关键字;

    细节2:针对不同接口,脚本需增加/减少特定的代码;(比如【修改】接口可能比【新增接口】需要多调用sleep方法,防止程序执行过快导致接口请求异常返回报错信息)

    细节3:成功执行该脚本后,会在二级模块名【/excel】里生成对应的【banner(包含接口返回值和断言结果).xlsx】,会包含每条接口用例的返回值和断言结果;

    5)第五,核对生成的数据

    1. 查看【banner(包含接口返回值和断言结果).xlsx-【创建banner】sheet页的 返回值和断言结果】,大概看一下返回值的数据是否正确;如果有错误的返回值,则继续排查和优化相关脚本;

    6)第六,执行单一入口函数

    步骤:

    1.配置根目录run.py相关参数信息;

    2.执行run.py

    3.生成html格式的测试报告;并,发送相关报告至指定邮箱;

    后期拓展:

    1.部署线上jenkins服务,部署本地/线上python环境,部署本地/线上wamp环境,CI持续集成;

    2.熟悉相关linux命令;优化相关脚本逻辑;

    3.部署线上禅道服务,实现主要功能:实时写入bug&获取bug清单&更改bug状态,下载最新包含符合筛选条件的bug的excel文档;

     python相关核心脚本如下:

     1 # coding:utf-8
     2 '''
     3 @file: test_002_update_banner_testcase.py
     4 @author: jingsheng hong
     5 @ide: PyCharm
     6 @createTime: 2019年07月29日  10点21分
     7 @contactInformation: 727803257@qq.com
     8 '''
     9 
    10 
    11 import unittest
    12 import ddt
    13 from data.b.banner.d_update_banner              import D_update_banner
    14 from controller.b.banner.c_update_banner        import C_update_Banner
    15 from expectedResult.b.banner.e_update_banner    import E_update_banner
    16 from validate.b.banner.v_update_banner          import V_update_banner
    17 from optimize.b.banner.o_update_banner          import O_update_banner
    18 from writeCellValue.b.banner.w_update_banner    import W_update_banner
    19 
    20 excel_data = D_update_banner().excel_data()
    21 
    22 @ddt.ddt
    23 class Test_update_banner(unittest.TestCase):
    24     '''【更新banner】接口的接口测试用例集合'''
    25 
    26     def setUp(self):
    27         pass
    28 
    29     def tearDown(self):
    30         pass
    31 
    32     @ddt.data(*excel_data)
    33     def test_update_banner(self,data):
    34         O_update_banner(data).sleep()
    35         O_update_banner(data).printAllParamsLog()
    36         expectResult    =  E_update_banner(data).update()
    37         actualResult    =  C_update_Banner(data).update()
    38         W_update_banner(data).writeReturnValue(actualResult)
    39         O_update_banner(data).printLog(expectResult,actualResult)
    40         compareResult   =  V_update_banner(expectResult,actualResult).compareResult()
    41         assertResult    =  V_update_banner(expectResult,actualResult).assertResult()
    42         W_update_banner(data).writeAssertResult(assertResult)
    43         self.assertTrue(compareResult)
    44 
    45 if __name__ =="__main__":
    46     unittest.main()

    php相关控制层脚本如下:

      1 <?php
      2 /**
      3  * Created by PhpStorm.
      4  * User: Administrator
      5  * Date: 2019/03/25
      6  * Time: 19:02
      7  */
      8 
      9 namespace appapicontroller;
     10 
     11 use appapicontrollerApiCommon;
     12 use appapicontrollerutilsFile;
     13 use appapimodelModelBanner;
     14 use appapivalidateV_Banner;
     15 use thinkDb;
     16 use thinkResponse;
     17 
     18 class Banner  extends ApiCommon
     19 {
     20 
     21     /**  后台查询banner列表(可传id获取单个banner信息)
     22      * @return 	hinkResponse
     23      */
     24     public function showBannerA(){
     25         $id = input('param.id');
     26         $page = input('param.page');
     27         $last = input('param.last');
     28         //实例化content,查询all
     29         $mBanner = new ModelBanner();
     30         $list = $mBanner->showBannerA($id,$page,$last);
     31         $count = $mBanner->countBanner();
     32         list_upload_image_path_format($list,'image');
     33         if ($list){
     34             $data = [
     35                 'code'=>0,
     36                 'count'=>$count==0?'':$count,
     37                 'data'=>$list,
     38             ];
     39             return response($data,0,array(),'json');
     40         }else{
     41             return api_list_not_more();
     42         }
     43     }
     44     //Banner排序接口
     45     public function sortBanner(){
     46         //获取到的id和sort值
     47         $param = input('param.');
     48         if (empty($param)){
     49             return api_param_error();
     50         }
     51         $all=[];
     52         for ($i = 0;$i<count($param['id']);$i++){
     53             //验证参数
     54             $validate = new V_Banner();
     55             if (!$validate->scene('sort')->check(['sort'=>$param['sort'][$i]])) {
     56                 return api_param_error($validate->getError());
     57             }
     58             $info['id']=$param['id'][$i];
     59             $info['sort']=$param['sort'][$i];
     60             $all[] = $info;
     61         }
     62         $banner = new ModelBanner();
     63         $res = $banner->saveAll($all);
     64         if ($res){
     65             return api_success('操作成功');
     66         }else{
     67             return api_error();
     68         }
     69     }
     70 
     71     /**  删除banner(传id删除单个banner或传数组批量删除)
     72      * @return 	hinkResponse
     73      */
     74     public function delBanner(){
     75         $id = input('param.id');
     76         $mBanner = new ModelBanner();
     77         $banner =$mBanner->delBanner($id);
     78         if ($banner){
     79             return api_success('成功删除');
     80         }else{
     81             return api_error();
     82         }
     83     }
     84     /**  创建banner
     85      * @return Response
     86      */
     87     public function creBanner(){
     88         //获取内容信息    id title img_path status  type
     89         $info = input('param.');
     90         //验证参数
     91         $validate =  new V_Banner();
     92         if (!$validate->scene('create')->check($info)) {
     93             return api_param_error($validate->getError());
     94         }
     95         if (strpos($info['sort'],'.')){
     96             return api_error('排序只能是整数');
     97         }
     98         $banner = new ModelBanner();
     99         $sort = $banner->where('sort',$info['sort'])->find();
    100         if ($sort){
    101             return api_error('此排序数字已经存在');
    102         }
    103         $file = new File();
    104         $files = $file->upload('Banner','image',true);
    105         //判断是否上传文件
    106         if ($files instanceof Response) {
    107             return api_error('没有上传图片');
    108         }
    109         //检查是否上传成功
    110         if ($files[0]['is_success']) {
    111             // 启动事务
    112             Db::startTrans();
    113             try {
    114                 $info['image'] = $files[0]['file_path'];
    115 
    116                 $res = $banner->save($info);
    117                 if ($res){
    118                     // 提交事务
    119                     Db::commit();
    120                     return api_success('创建成功!');
    121                 }
    122             } catch (Exception $e) {
    123                 // 回滚事务
    124                 Db::rollback();
    125                 return api_success('创建失败!');
    126             }
    127         }else{
    128             return api_error($files[0]['error_msg']);
    129         }
    130 
    131     }
    132     /** 修改banner
    133      * @return 	hinkResponse
    134      */
    135     public function upBanner(){
    136 
    137         //获取内容信息    id title img_path status  type
    138         $info = input('param.');
    139         //验证参数
    140         $validate =  new V_Banner();
    141         if (!$validate->scene('update')->check(input('param.'))) {
    142             return api_param_error($validate->getError());
    143         }
    144         if (strpos($info['sort'],'.')){
    145             return api_error('排序只能是整数');
    146         }
    147         $banner = new ModelBanner();
    148         $sort = $banner->where('sort',$info['sort'])->find();
    149         if ($sort){
    150             if (!$banner->where('sort',$info['sort'])->find($info['id'])){
    151                 return api_error('此排序数字已经存在');
    152             }
    153         }
    154         $file = new File();
    155         $files = $file->upload('Banner','image',true);
    156 
    157         //判断是否上传文件
    158         if (!$files instanceof Response) {
    159             //判断是否符合大小和格式
    160             if (!$files[0]['is_success']) {
    161                 return api_error($files[0]['error_msg']);
    162             }
    163             $info['image'] = $files[0]['file_path'];
    164         }
    165         // 启动事务
    166         Db::startTrans();
    167         try {
    168             $mBanner = new ModelBanner();
    169             $res = $mBanner->save($info,['id'=>$info['id']]);
    170             if ($res){
    171                 // 提交事务
    172                 Db::commit();
    173                 return api_success('修改成功!');
    174             }
    175         } catch (Exception $e) {
    176             // 回滚事务
    177             Db::rollback();
    178             return api_error('修改失败!');
    179         }
    180     }
    181 
    182 
    183 
    184     /**  banner上下线功能
    185      * @return Response
    186      */
    187     public function togStatus(){
    188         $id = input('param.id');
    189         if (empty($id)){
    190             return api_param_error();
    191         }
    192         $mBanner = new ModelBanner();
    193         $result=$mBanner->togStatus($id);
    194         if($result){
    195             return api_success("操作成功!");
    196         }else{
    197             return api_error();
    198         }
    199 
    200     }
    201 
    202     /**  测试
    203      * @return array|false|PDOStatement|string|	hinkModel
    204      */
    205     function f(){
    206         $id = input('param.');
    207         $banner = new ModelBanner();
    208         $item = $banner->find($id);
    209         return $item;
    210     }
    211 
    212     public function image(){
    213 //        $arr = input('param.');
    214 //        dump($arr);
    215 //        return $arr;
    216         return input('param.');
    217         return $_POST['title'];
    218         return $_FILES;
    219         $files = request()->file('file');
    220         return $files;
    221     }
    222 
    223 }

    待更新 ...

  • 相关阅读:
    第15章 RCC—使用HSE/HSI配置时钟—零死角玩转STM32-F429系列
    第14章 启动文件详解—零死角玩转STM32-F429系列
    第13章 GPIO-位带操作—零死角玩转STM32-F429系列
    第12章 GPIO输入-按键检测—零死角玩转STM32-F429系列
    使用Vmware过程中,突然网络连接不上问题
    Yaf自定义autoload以实现Model文件和Controller文件命名区分
    Yaf学习过程中遇到的问题小记
    网页出现横向滚动条的原因可能是使用bootstrap不当引起
    微信小程序开发(一)
    nginx 启动报错找不到nginx.pid文件
  • 原文地址:https://www.cnblogs.com/xiamen-momo/p/11368379.html
Copyright © 2011-2022 走看看