接口自动化测试流程和相关准备工作

第一步: 拿到需求文档、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: [email protected]
 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 app\api\controller\b;
 10 
 11 use app\api\controller\ApiCommon;
 12 use app\api\controller\utils\File;
 13 use app\api\model\ModelBanner;
 14 use app\api\validate\V_Banner;
 15 use think\Db;
 16 use think\Response;
 17 
 18 class Banner  extends ApiCommon
 19 {
 20 
 21     /**  后台查询banner列表(可传id获取单个banner信息)
 22      * @return \think\Response
 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 \think\Response
 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 \think\Response
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|\think\Model
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 }

待更新 ...

猜你喜欢

转载自www.cnblogs.com/xiamen-momo/p/11368379.html