第一步: 拿到需求文档、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 }
待更新 ...
原文地址:https://www.cnblogs.com/xiamen-momo/p/11368379.html