再次强调,因为视频中实战地址已无法访问,建议大家根据原理,用自己公司的业务逻辑、代码来练手。本人公司使用pyhon2.7,所以语法可能与3.x不同
目标
- 复习单元测试:引入单元测试、html测试报告、断言结果
- 引入超继承(二选一)
- 引入ddt(二选一)
- 添加一个字段存入测试结果
- 引入try...except...finally
- 完成用例的可配置化,想跑哪条用例,通过配置文件配置好
- 难点:
- 同时跑多个模块怎么跑
复习单元测试
使用unittest,测试我们自己封装的http请求类-HttpRequest,目的一:学习如何进行单元测试,目的二:写出咱们的各个接口。
-
新建
test_http_request.py
编写测试用例import json import unittest from common.public.http_request import HttpRequest class TestHttpRequest(unittest.TestCase): def setUp(self): pass def tearDown(self): pass #登录接口 def test_login(self): payload = {"user_phone": "1801923****", "device_model": "iphone7"} res=HttpRequest().http_request("1.6.0", "android", "785c6fee0e4488ca412a5afc9a00e9d8","/login","post",payload) self.assertEqual(200,res.status_code) #添加断言 print ("获得的结果是:",res.json())
-
在
run.py
文件中加载用例、执行用例import unittest from common.public.test_http_request import TestHttpRequest suite=unittest.TestSuite() suite.addTest(TestHttpRequest("test_login")) #添加用例 #执行用例 runner=unittest.TextTestRunner() runner.run(suite)
-
使用HTMLTestRunner生成HTML测试报告
下载地址:http://tungwaiyip.info/software/HTMLTestRunner.html
使用:
- HTMLTestRunner是Python标准库的unittest模块的扩展,无法通过pip安装 - 下载HTMLTestRunner.py放在lib目录下 - 导入模块 `import HTMLTestRunner`
注意点:
-
使用python2.x可以直接使用,python3.x需要进行改造。
-
在文章python基础之类与对象的应用-单元测试unittest模块 中讲过了。
代码改造为使用HTMLTestRunner生成HTML测试报告:
# encoding:utf-8 import sys reload(sys) sys.setdefaultencoding('utf8') #python2.7需要添加,否则会因编码问题报错 import HTMLTestRunner import unittest from common.public.test_http_request import TestHttpRequest suite=unittest.TestSuite() suite.addTest(TestHttpRequest("test_login")) #添加用例 with open("test_result/rusult.html","wb") as file: #执行用例 runner=HTMLTestRunner.HTMLTestRunner(stream=file,verbosity=2,title="测试HttpRequest类") runner.run(suite)
获得的html格式的测试报告:
image -
引入ddt
引入ddt的目的是为了数据分离、数据驱动测试。所谓数据驱动,从它的定义来看,就是数据的改变从而驱动自动化测试的执行,最终引起测试结果的改变。说的直白些,就是参数化的应用。值得注意的是,ddt适用于测试数据是列表嵌套列表或者列表嵌套字典。
-
给测试数据中新增一列expected,用于添加断言的预期结果
image -
修改do_excel.py,读取expected的数据:
get_data()方法中新增:
sub_data["expected"] = sheet.cell(i, 6).value
write_back_data()方法中修改:sheet.cell(i,7).value=value
-
改造测试用例
test_http_request.py
import unittest from common.public.http_request import HttpRequest from ddt import ddt,data from common.public.do_excel import DoExcel test_data=DoExcel().get_data("../../test_data/test_data.xlsx","Sheet1") @ddt class TestHttpRequest(unittest.TestCase): def setUp(self): pass def tearDown(self): pass @data(*test_data) def test_login(self,item): #添加item,即测试数据 res=HttpRequest().http_request(item["url"],item["method"],eval(item["payload"])) self.assertEqual(str(item["expected"]),str(res.status_code)) print "获得的结果是:",res.json()
-
改造
run.py
import HTMLTestRunner import unittest from common.public.test_http_request import TestHttpRequest suite=unittest.TestSuite() loader=unittest.TestLoader() suite.addTest(loader.loadTestsFromTestCase(TestHttpRequest)) with open("test_result/rusult.html","wb") as file: #执行用例 runner=HTMLTestRunner.HTMLTestRunner(stream=file,verbosity=2,title="测试HttpRequest类") runner.run(suite)
-
问题:因为
test_http_request.py
和run.py
不是一个层级,所以这样运行run.py
会报错,找不到test_data.xlsx
,所以在test_http_request.py
中的test_data
地址改为绝对路径。but,如果设置为绝对路径,那么在自己电脑上能跑,在别人电脑上可能会失败。 -
解决方案:路径可配置
- os.path.split()函数,将文件名和路径分割开
- os.path.split(path)[0],取上级目录
- os.path.join(path,*paths),拼接路径不用加/
import os """专门读取路径的值""" #获取当前文件所在绝对路径,包括模块名,__file__表示是当前文件本身 path=os.path.realpath(__file__) #获取顶级目录的绝对路径 project_path=os.path.split(os.path.split(os.path.split(path)[0])[0])[0] #获取测试数据的绝对路径 test_data_path=os.path.join(project_path,"test_data","test_data.xlsx") #获取测试报告的绝对路径 test_result_path=os.path.join(project_path,"test_result","html_report","result.html")
- 使用
project_path.py
中的路径- 引入
project_path.py
:from common.public.project_path import *
-
test_http_request.py
中:test_data=DoExcel().get_data(test_data_path,"Sheet1")
-
run.py
中:with open(test_result_path,"wb") as file:
- 引入
-
添加字段存入测试结果
1.result字段存放response.json()
添加:DoExcel().write_back_data(test_data_path,"Sheet1",item["case_id"]+1,str(res.json))
# encoding: utf-8
import json
import unittest
from common.public.http_request import HttpRequest
from ddt import ddt,data
from common.public.do_excel import DoExcel
from common.public.project_path import *
test_data=DoExcel().get_data(test_data_path,"Sheet1")
@ddt
class TestHttpRequest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
@data(*test_data)
def test_login(self,item):
res=HttpRequest().http_request(item["url"],item["method"],eval(item["payload"]))
self.assertEqual(str(item["expected"]),str(res.status_code))
#将res.json写入excel中
DoExcel().write_back_data(test_data_path,"Sheet1",item["case_id"]+1,str(res.json))
print "获得的结果是:", res.json()
- 问题:一,报错时,因为执行不到print语句,从result.html中无法看出时什么问题。二,如果报错时,res.json()就无法写入到excel文件中。
- 解决:使用
finally
,是无论有没有捕获到异常都执行finally
下的代码(代码在后面展示)
2.新增一个TestResult字段存放测试结果
与result字段不同,result字段存放的是response.json(),TestResult字段存入Pass/Failed,表示用例成功还是失败。写入excel后可以直接通过excel筛选,只查看为Failed的用例
-
首先在
imagetest_data.xlsx
中添加字段TestResult -
改造测试用例
test_http_request.py
test_data=DoExcel().get_data(test_data_path,"Sheet1") @ddt class TestHttpRequest(unittest.TestCase): def setUp(self): pass def tearDown(self): pass @data(*test_data) def test_login(self,item): res=HttpRequest().http_request(item["url"],item["method"],eval(item["payload"])) #捕获异常 try: self.assertEqual(str(item["expected"]),str(res.status_code)) #如果没有异常, TestResult="Pass" TestResult="Pass" except AssertionError as e: #如果有异常, TestResult="Failed" TestResult = "Failed" print "测试用例失败,{0}".format(e) raise e #无论是否捕获异常都执行 finally: print "获得的结果是:", res.json() DoExcel().write_back_data(test_data_path,"Sheet1",item["case_id"]+1,str(res.json())
-
改造
do_excel.py
写入TestResult字段def write_back_data(self,file_name,sheet_name,i,result,TestResult): wb=load_workbook(file_name) sheet=wb[sheet_name] sheet.cell(i,7).value=result sheet.cell(i,8).value=TestResult wb.save(file_name) #保存
同时,
test_http_request.py
中添加TestResult字段:
DoExcel().write_back_data(test_data_path,"Sheet1",item["case_id"]+1,str(res.json(),TestResult)
用例可配置
实现多个用例同时执行、多个模块同时执行
配置文件效果:
[MODE]
mode={"login":"all",
"register":[1,2,4,5],
"recharge":[1,2]
}
通过option和value,配置不同的用例,option是对应的模块(使用不同的sheet放置不同的模块,对应sheetname),value的all代表该模块所有用例,列表里的数字代表case_id,
- 新建一个配置文件
case.conf
- 写一个读取配置文件的类
import configparser
class ReadConfig:
@staticmethod
def get_config(file_path,setion,option):
cf=configparser.ConfigParser()
cf.read(file_path)
return cf[setion][option]
if __name__ == '__main__': #测试
from common.public import project_path
print ReadConfig.get_config(project_path.case_config_path,"MODE","mode")
控制台输出:
image-
改造
do_excel.py
class DoExcel: @staticmethod #静态方法,直接调用 def get_data(file_name): wb=load_workbook(file_name) mode=eval(ReadConfig.get_config(project_path.case_config_path,"MODE","mode")) test_data=[] #循环配置文件中的mode的key for key in mode: sheet=wb[key] #如果为all,跑所有的case if mode[key]=='all': for i in range(2,sheet.max_row+1): row_data={} row_data["case_id"] = sheet.cell(i, 1).value row_data["url"]=sheet.cell(i,2).value row_data["method"]=sheet.cell(i,3).value row_data["payload"]=sheet.cell(i,4).value row_data["title"]=sheet.cell(i,5).value row_data["expected"] = sheet.cell(i, 6).value #读取sheet_name row_data["sheet_name"]=key test_data.append(row_data) else: #如果不是all,按case_id跑 for case_id in mode[key]: row_data = {} row_data["case_id"] = sheet.cell(case_id+1, 1).value row_data["url"] = sheet.cell(case_id+1, 2).value row_data["method"] = sheet.cell(case_id+1, 3).value row_data["payload"] = sheet.cell(case_id+1, 4).value row_data["title"] = sheet.cell(case_id+1, 5).value row_data["expected"] = sheet.cell(case_id+1, 6).value row_data["sheet_name"] = key test_data.append(row_data) return test_data @staticmethod def write_back_data(file_name,sheet_name,i,result,TestResult): wb=load_workbook(file_name) sheet=wb[sheet_name] sheet.cell(i,7).value=result sheet.cell(i,8).value=TestResult wb.save(file_name) #保存
-
改造测试用例
test_http_request.py
修改sheet_name参数化:
DoExcel().write_back_data(test_data_path,item["sheet_name"],item["case_id"]+1,str(res.json()),TestResult)
网友评论