# 08 | 测试数据:是不是可以把所有的参数都保存到Excel中? 你好,我是陈磊。 课程到现在,我们已经一起从接口测试思维的训练,走到了接口测试技术的训练,随着学习的不断深入,你应该也有了一个自己的测试框架,虽然这个框架可能还很简陋。但是任何事情不管多晚开始,都好于从未开始,因此学到现在,你已经迈出了接口测试以及其测试技术的第一步。 做任何事情,从零到一都需要莫大的勇气和坚定的决心,在这个过程中,你要将自己挪出舒适区,进入一个陌生的领域,这确实很难。但如果你和我一起走到了这一节课,那么我要恭喜你,你已经完成了接口测试从零到一的转变,后续从一到无穷大,你只需要随着时间去积累经验就可以了。 如果把接口测试比喻成要炒一盘菜的话,那么我在之前的全部课程中,重点都是在讲解如何完成接口测试,也就是教你如何炒菜。我也教过你如何解决接口测试的需求,为你提供了解决问题的能力和手段,这也就是在帮你建造一个设备齐全的厨房,帮你一起完成接口测试任务 。 有了精致的厨房后,我也告诉了你要怎么制作顶级的厨具,也就是接口测试的技术方法和实践方式。这些厨具既有锅碗瓢盆,也有刀勺铲叉,这里的锅碗瓢盆就是你的测试框架,刀勺铲叉就是你使用框架完成的测试脚本,这其中既包含了单接口的测试脚本,也包含了业务逻辑多接口测试脚本。 那么如果想炒菜你还需要准备什么呢?毫无疑问那就是菜,所谓“巧妇难为无米之炊”,即使你有高超的手艺,有世界顶级的厨具,但如果没有做菜的原材料,那也没办法把菜做出来,就算是世界顶级大厨,也无法完成这样的任务。 今天我就顺着这个思路,和你讲讲菜的准备,也就是接口测试的数据准备工作。 ## 测试数据的好处:打造自动化测试框架 随着你不断封装自己的测试框架,你的框架就始终处于等米下锅这样一种的状态,而米就是测试数据。我在之前的课程中,都是将测试数据直接写在代码里,赋值给一个变量,然后通过接口测试逻辑来完成测试。 说到这,我还是把我们之前用过的”战场“这个系统拿出来,看一看它“选择武器”这个接口测试脚本(你可以回到[04](https://time.geekbang.org/column/article/195483)中查看),虽然你现在对怎么撰写、怎么封装类似的接口脚本都已经烂熟于心,但我们还是先看一下它的代码段: ``` # uri_login存储战场的选择武器 uri_selectEq = '/selectEq' # 武器编号变量存储用户名参数 equipmentid = '10003' # 拼凑body的参数 payload = 'equipmentid=' + equipmentid response_selectEq = comm.post(uri_selectEq,params=payload) print('Response内容:' + response_selectEq.text) ``` 这就是你通过自己的Common类改造后的测试框架,但是现在,它还不能是算是一个完美的框架,为什么呢? 这是因为,你现在的参数都是直接通过equipmentid变量赋值的,在做测试的时候,你还需要不断修改这个参数的赋值,才能完成接口的入参测试,这不是一种自动化的测试思路。 因此,你需要将数据封装,通过一种更好的方式,将数据存储到一种数据存储文件中,这样代码就可以自行查找对应的参数,然后调取测试框架执行测试流程,接着再通过自动比对返回预期,检验测试结果是否正确。 这样做有两个好处。 1. 无人值守,节省时间和精力。我们将所有的参数都存储到外部存储文件中,测试框架就可以自行选择第一个参数进行测试,在完成第一个测试之后,它也就可以自行选择下一个参数,整个执行过程是不需要人参与的。否则的话,我们每复制一组参数,就要执行一次脚本,然后再人工替换一次参数,再执行一次脚本,这个过程耗时费力,而且又是一个纯人工控制的没什么技术含量的活动。 2. 自动检测返回值,提高测试效率。如果你用上面的代码段完成接口测试,就要每执行一次,人工去观察一次,看接口的返回是不是和预期一致,人工来做这些事情,不只非常耗费时间,效率也很低下。但是通过代码完成一些关键匹配却很容易,这可以大大提高测试效率,快速完成交付。 怎么样,看到这些好处,你是不是也想马上给你的框架加上数据处理的部分了呢? ## 如何选取测试数据 现在我们就马上开始动手,为你的框架加上参数类。 首先,你先要定义一种参数的存储格式。那么我想问你的是,要是让你选择把数据储存在一个文件中,你会选择什么格式的文件呢? 我相信你肯定和我的选择一样,用Excel。因为目前来看,Excel是在设计测试用例方面使用最多的一个工具,那么我们也就可以用Excel作为自己的参数存储文件。 但在动手之前,你也应该想到,你的参数文件类型不会是一成不变的Excel,未来你也有可能使用其他格式的参数文件,因此在一开始你还要考虑到参数类的扩展性,这样你就不用每多了一种参数文件存储格式,就写一个参数类,来完成参数的选取和调用了。 那么如何选取和调用参数呢?你可以看看我设计的参数类: ``` import json import xlrd class Param(object): def __init__(self,paramConf='{}'): self.paramConf = json.loads(paramConf) def paramRowsCount(self): pass def paramColsCount(self): pass def paramHeader(self): pass def paramAllline(self): pass def paramAlllineDict(self): pass class XLS(Param): ''' xls基本格式(如果要把xls中存储的数字按照文本读出来的话,纯数字前要加上英文单引号: 第一行是参数的注释,就是每一行参数是什么 第二行是参数名,参数名和对应模块的po页面的变量名一致 第3~N行是参数 最后一列是预期默认头Exp ''' def __init__(self, paramConf): ''' :param paramConf: xls 文件位置(绝对路径) ''' self.paramConf = paramConf self.paramfile = self.paramConf['file'] self.data = xlrd.open_workbook(self.paramfile) self.getParamSheet(self.paramConf['sheet']) def getParamSheet(self,nsheets): ''' 设定参数所处的sheet :param nsheets: 参数在第几个sheet中 :return: ''' self.paramsheet = self.data.sheets()[nsheets] def getOneline(self,nRow): ''' 返回一行数据 :param nRow: 行数 :return: 一行数据 [] ''' return self.paramsheet.row_values(nRow) def getOneCol(self,nCol): ''' 返回一列 :param nCol: 列数 :return: 一列数据 [] ''' return self.paramsheet.col_values(nCol) def paramRowsCount(self): ''' 获取参数文件行数 :return: 参数行数 int ''' return self.paramsheet.nrows def paramColsCount(self): ''' 获取参数文件列数(参数个数) :return: 参数文件列数(参数个数) int ''' return self.paramsheet.ncols def paramHeader(self): ''' 获取参数名称 :return: 参数名称[] ''' return self.getOneline(1) def paramAlllineDict(self): ''' 获取全部参数 :return: {{}},其中dict的key值是header的值 ''' nCountRows = self.paramRowsCount() nCountCols = self.paramColsCount() ParamAllListDict = {} iRowStep = 2 iColStep = 0 ParamHeader= self.paramHeader() while iRowStep < nCountRows: ParamOneLinelist=self.getOneline(iRowStep) ParamOnelineDict = {} while iColStep