前面我们已经实现了用unittest框架编写测试用例,实现了请求接口的封装,这样虽然已经可以完成接口的自动化测试,但是其复用性并不高。
我们看到每个方法(测试用例)的代码几乎是一模一样的,试想一下,在我们的测试场景中,一个注册接口有可能会有十几条到几十条测试用例,如果每组数据都编写一个方法,这样将会有更多的重复项代码,不仅执行效率不高,也不好维护。
接下来将会对框架进行优化,采用数据驱动方式,1)把测试数据用excel表格管理起来,代码做封装,2)用ddt来驱动测试,两部分相互独立。
1.数据与代码分离:excel管理测试数据
在上一节的代码中, test_register.py 模块中,定义了三个方法(三个测试用例),每一个测试用例都需要提供一组测试数据:url,params,method,expect_res等,不利于修改和构建,现在我们新建 excel文件(TestData.xlsx )。如下:
自动化用例设计:
关于自动化测试用例的设计,是基于对接口业务流程的熟悉,只有熟悉业务流程,才能设计出较好的自动化测试数据。这边要考虑的点很多,
- id:用例编号,从1开始,唯一
- module:接口模块
- case_name:用例名称
- method:请求类型
- url:接口地址信息
- params:请求参数
用例设计好了,这边我遇到了三个问题:
第一:如何读取测试数据?
第二:读取的测试数据,应该存储成什么格式?
第三:数据如何传递?
当然,用例的设计对于越复杂的场景,考虑的就会越多,其中有一点很重要,尽量保持用例的独立性,用例之间的关联性不要太强,避免一条用例的失败,导致其他用例也无法执行。
xlrd 使用
安装xlrd第三方库,pip install xlrd.from xlrd import open_workbook
wb = open_workbook("TestData.xlsx") # 打开excel sh = wb.sheet_by_name("register") # 定位工作表 print(sh.row_values(0)) # 输出第1行的所有值(列表格式) print(dict(zip(sh.row_values(0),sh.row_values(1)))) # 将数据和标题组成字
for i in range(sh.nrows): print(sh.row_values(i))
封装读取excel操作 -如何读取测试数据?
实现代码如下:
from xlrd import open_workbook class Doexcel(): def excel_data_list(self, filename, sheetname): data_list = [] wb = open_workbook(filename) # 打开excel sh = wb.sheet_by_name(sheetname) # 定位工作表 header = sh.row_values(0) # 获取标题行的数据 for i in range(1, sh.nrows): # 跳过标题行,从第二行开始获取数据 col_datas = dict(zip(sh.row_values(0), sh.row_values(i))) # 将标题和每一行的数据,组装成字典 data_list.append(col_datas) # 将字典添加到列表中 ,列表嵌套字典,相当于每个字典的元素都是一个列表(也就是一行数据) return data_list def get_test_data(self, data_list, case_id): ''' :param data_list: 工作表的所有行数据 :param case_id: 用例id,用来判断执行哪几条case。如果id=all ,那就执行所有用例;否则,执行列表参数中指定的用例 :return: 返回最终要执行的测试用例 ''' if case_id == 'all': final_data = data_list else: final_data = [] for item in data_list: if item['id'] in case_id: final_data.append(item) return final_data
if __name__ == '__main__':
data_list=Doexcel().excel_data_list('F:\JialiProgramfile\serviceX_API_Test\Test_datas\TestData.xlsx','register')
final_data=Doexcel().get_test_data(data_list, [1,2,3])
print(final_data)
执行结果
[{'method': 'post', 'expect_res': 200.0, 'actual_res': '', 'id': 1.0, 'test_res': '', 'case_name': 'test_register_normal', 'url': 'http://27.154.55.14:8180/api/fcb2bcrm/webRegister', 'module': 'register', 'params': '{"LoginAccount":"test01@gamil.com","Password":"123456","Type":"Pro"}'},
{'method': 'post', 'expect_res': 400.0, 'actual_res': '', 'id': 2.0, 'test_res': '', 'case_name': 'test_register_existing', 'url': 'http://27.154.55.14:8180/api/fcb2bcrm/webRegister', 'module': 'register', 'params': '{"LoginAccount":"test01@gamil.com","Password":"123456","Type":"Pro"}'},
{'method': 'post', 'expect_res': 400.0, 'actual_res': '', 'id': 3.0, 'test_res': '', 'case_name': 'test_register_invalid_email', 'url': 'http://27.154.55.14:8180/api/fcb2bcrm/webRegister', 'module': 'register', 'params': '{"LoginAccount":"test01@gamil","Password":"123456","Type":"Pro"}'}]
2.ddt 数据驱动- 如果传递数据?
数据驱动,个人理解就是测试数据的参数化
1)安装第三方库 : pip install ddt
2)引入ddt模块:from ddt import ddt,data
3)test_register.py 代码加入ddt
# 导入 import unittest import requests from Common.http_request import HttpRequest from ddt import ddt,data # 引入ddt模块 from Common.do_excel import * test_data = Doexcel().excel_data_list('F:\JialiProgramfile\serviceX_API_Test\Test_datas\TestData.xlsx','register') #读取工作表 register 的所有数据 ,返回的是个列表嵌套字典格式 @ddt class TestRegister (unittest.TestCase): # 类必须以Test开头,继承TestCase def setUp(self): print("======开始执行测试用例======") def tearDown(self): print("======测试用例执行完毕======") # 测试用例 @data(*test_data) def test_register(self, data_itme): # data_item 就是每一组测试数据(字典的形式) # 发送请求 res = HttpRequest().http_request(data_itme['url'],eval(data_itme['params']),data_itme['method']) # 断言: try: self.assertEqual(int(data_itme['expect_res']), res.status_code) except AssertionError as e: print('Failed') raise e # 注意一定要抛出异常
3.测试结果写回 - 扩展doExcel类
在excel表格中,有 两列的值是需要在执行完测试用例后写回的:实际结果和测试结果。
def write_back_result(self, filename, sheetname, row, actual_res, test_result): ''' :param filename: 文件名 :param sheetname: 要写回数据的表格 :param row: 要写回的行数 :param actual_res: 实际结果 :实际结果是第8列,测试结果是第9列 ,比如:(2,8)(2,9) :param test_result: 测试结果 :pass/failed :return: ''' wb = load_workbook(filename) sheet = wb[sheetname] sheet.cell(row,8).value = actual_res sheet.cell(row,9).value = test_result wb.save(filename)
在test_register.py文件中,每次执行完一组测试数据,调用 write_back_result()方法,将所需要的参数传递进去。