总说框架框架不够接地气,那就简单的理解为目录,如下图是我的目录:
这是一般通用性工程目录结构,
在大脑中应该有一个基本的初型,可能会用到哪些模块,以下是需要用到的模块,淡定,下面会一一讲到:
其中只有cx_Oracle和HTMLTestRunner_PY3容易出错,都不是通过pip install xxx来进行安装的,如下链接查看:
把工程的基本目录结构搭建完后,一般想到的第一个问题是,应该从那里入手。那一般情况下,我会从公共类入手:
4-1、比如ReadConfig(配置文件读取),在config中建一个config.ini的文件用来存放配置文件,我们用过很多C\S客户端类软件,都有一个类似的config,应该先想好把哪些内容放到配置文件中:
4-2、在public中新建ReadConfig.py(代码【需要代码的可移步到】如下),用来读取配置文件,使用到模块:configparser,可自行查找使用方法,强调一下,写完一个类,都应该在:main里测试一下,保存单个功能是正常的。如此也有阶段性成就感:
4-3、公共类Log.py,这是所有项目必须的公共类,在必要的地方记录log有助于查找问题所在,使用到的模块为logging,一般新手都有同一个问题:我知道应该打log,但log怎么打,打在哪里,打些什么东西呢?
log记录内容不应该太过单一,可以在必要的步骤结点添加log,也可以在try except里打印error,也可打印一个用例的结果数据等等,只要有助你查找问题,查看流程都可以。
4-4、这里要说明一下,其实公共类编写没有一定的顺序,只是我一般会先写config,因为log里可能用到config;再写log,一般其它公共类也会打log。ReadConfig.py和Log.py写完后, 再想有哪些是接口测试需要用到的方法或参数。我想到一个,接口测试时,测试数据来源是哪里,难倒每次先手工测一下拿到数据写到我们的test_case_data里吗?那有些数据是动态变化的怎么办。这时就应该从用户的角度去想这个问题,哪些数据是可以从用户的角度去拿到的。根据用户能拿到的数据再去查找我们需要的数据,比如:接口post data中有一个参数:user_id,那我们应该从用户的角度去拿到用户名,再通过数据库查询到user_id,这时,就需要写一个数据库操作类,一般只需要查询,也可能把类完善到增删改查。
python中对mysql和Oracle都有相应的模块支持,Mysql相对简单一些,我使用的Oracle支持模块cx_Oracle,模块安装如上所示,
4-5、终于可能写到HTTP公共类啦,此类的作用是:初始化url、headers、data、get请求、post请求等,编写完后在main中测试一下get、post发送是否正常:
4-6、还有一个get_data.py的公共文件,我会放一些现在没想到,之后需要的公共方法(比如:获取excel数据,获取请求的json数据,获取cookie\session数据),还有一些真实业务场景需要的方法:
4-7、别忘记把HTMLTestRunner_PY3.py复制到public目录下,后期需要进行调试,到此,我用到的公共类、方法就写完了。
写完公共类后,再从宏观的想想整个测试流程:手工测试时,我们会写到测试用例(那种excel的形式),这里把表中一条条的数据作为输入,经过公共类处理,传给test_case,run执行入口负责调试所有test_case并执行,再把执行结果以html、excel、log的形式输出:如图所示:
从excel中一行行的数据执行,一定就有参数化的问题,需要unittest自己循环去执行,此时python构造器的优势就体现出来啦:使用在类前修饰:
@paramunittest.parametrized(*instrBackRecordJsonList_xls),详见代码:
注:要求与excel中的列一一对应
import unittest import paramunittest from public import ReadConfig from public.Log import MyLog from public.MyHttp import Http from public.GetData import get_xls from public.OracleOperation import MyOracle from public.GetData import get_cookie from public.GetData import get_until rc = ReadConfig.ReadConfig("808_config.ini") h = Http() orc = MyOracle() instrBackRecordJsonList_xls = get_xls("instruct_data.xlsx", "instruct") print(instrBackRecordJsonList_xls) @paramunittest.parametrized(*instrBackRecordJsonList_xls) class TestInstructTypeParam(unittest.TestCase): def setParameters(self, case_name, method, vehicleIds, cmdCode, cmdVal, sendTitle, paramCode, paramName, Id): self.case_name = str(case_name) self.method = str(method) self.vehicleIds = str(vehicleIds) self.cmdCode = str(cmdCode) self.cmdVal = str(cmdVal) self.sendTitle = str(sendTitle) self.paramCode = str(paramCode) self.paramName = str(paramName) self.Id = str(Id) self.return_json = None self.result_json = None self.session_id = get_cookie() def description(self): """ 描述 :return: """ self.case_name def setUp(self): """ 测试用例开始前布置环境,查询车辆的vehicleId,记录日志 :return: """ self.log = MyLog.get_log() self.logger = self.log.get_logger() sql = "SELECT VEHICLE_ID FROM V_VEHICLEINFO WHERE ID_NUMBER LIKE '%s'" % rc.get_vehicle("ID_NUMBER") vid = orc.executeSQL(sql) self.vehicleIds = orc.get_one(vid)[0] print(self.vehicleIds) def test_Instruct(self): # 1、设置url url = rc.get_interface_url("instructTypeParam") h.set_url(url) # self.session_id = "Thp26DXAKwSrjGroUXpFD9lPwbV698WQVYT0KPi6vKK72MEeAuGY!-1564142059" # 2、设置header header = {"ocde": "0", "username": "system", "password": "system%40123", "orgCode": "B", "JSESSIONID": self.session_id , "TOPMENU": "%2Fhome.do"} h.set_headers(header) # 3、设置data,请求参数 data = {"vehicleIds": self.vehicleIds, "cmdCode": self.cmdCode, "cmdVal": self.cmdVal, "sendTitle": self.sendTitle, "paramCode": self.paramCode, "paramName": self.paramName, "id": self.Id} h.set_data(data) # 4、发送请求 res = h.post() if res.status_code == 200: self.return_json = h.post().json() print(self.return_json) # 5、检查结果 # 先获取指令反馈结果,调用get_until()方法 try: rollcallId = self.return_json["text"] except TypeError as e: self.logger.error(e) self.result_json = get_until(self.vehicleIds, rollcallId, 65, self.session_id) self.checkResult() else: self.logger.error("地址%s的Status Code:%s" % (url, res.status_code)) def checkResult(self): if self.result_json: if self.result_json[0]["result"] == 0: msg = "指令(%s):成功!终端返回信息:[%s]" % (self.case_name, self.result_json[0]["backInfo"]) elif self.result_json[0]["result"] == 1: msg = "指令(%s):超时!终端返回信息:[%s]" % (self.case_name, self.result_json[0]["backInfo"]) else: msg = "指令(%s):未发送!终端返回信息:[%s]" % (self.case_name, self.result_json[0]["backInfo"]) self.assertEqual(self.result_json[0]["result"], 0, msg=msg) def tearDown(self): # 用例执行后把self.return_json记录要日志中 self.log.build_case_line(self.case_name, **self.return_json) if __name__ == '__main__': # 在unittest.main()中加 verbosity 参数可以控制输出的错误报告的详细程度,默认是 1, # 如果设为 0,则不输出每一用例的执行结果,即没有上面的结果中的第1行;如果设为 2,则输出详细的执行结果 unittest.main(verbosity=2)
写了多个test_case后,怎么把他们组装起来,比如,获取所有test_case,以一定的顺序去执行,或有些执行有些不执行,应该怎么去调度呢?
建议多了解一下TestSuite测试集,在run中添加获取所有测试集的函数,最后run方法中拿到所有测试集,输出html报告,执行,打印log,finally中close()文件。代码如下:
# -*- coding:utf-8 -*- #__author:Administrator #date: 2018/1/3 import os import unittest from public import ReadConfig from public.Log import MyLog from public import HTMLTestRunner_PY3 class RunTest: def __init__(self): """ 初始化需要的参数 :return: """ global log, logger, resultPath # log初始化 log = MyLog.get_log() self.logger = log.get_logger() # 定义结果保存路径 self.resultPath = log.get_report_path() # 取得config\caselist.txt文件路径 self.caseListFile = os.path.join(ReadConfig.conf_path, "caselist.txt") # 取得test_case文件路径 self.caseFile = os.path.join(ReadConfig.proDir, "test_case") # 定义一个空列表,用于保存类名 self.caseList = [] def get_case_list(self): """ 获取config\caselist.txt中的每一行,以#号开头的除外 且添加到列表self.caseList进行返回 :return: self.caseList """ f = open(self.caseListFile) for value in f.readlines(): if value != '' and not value.startswith("#"): self.caseList.append(value.replace("\n", "")) f.close() return self.caseList def get_case_suite(self): """ 获取测试集 :return: """ # 获取config\caselist.txt中的每一行 self.get_case_list() # 定义测试集对象 test_suite = unittest.TestSuite() # 初始化一个列表,存在所有的测试模块 suite_module = [] # 获取className ,把所有case中测试集添加到suite_module列表中 for case in self.caseList: case_name = case.split("/")[-1] # print(case_name + ".py") discover = unittest.defaultTestLoader.discover(self.caseFile, pattern=case_name + ".py", top_level_dir=None) suite_module.append(discover) # 获取列表中所有的测试模块,添加到测试集test_suite中 if len(suite_module) > 0: for suite in suite_module: for test_name in suite: test_suite.addTest(test_name) else: return None return test_suite def run(self): try: # 获取测试集 suit = self.get_case_suite() print("suit:", suit) # 判断测试集是否为None if suit is not None: self.logger.info("********TEST START********") f = open(self.resultPath, 'wb') # 使用HTMLTestRunner输出html报告 runner = HTMLTestRunner_PY3.HTMLTestRunner(stream=f, title='Test Report', description='Test Description', verbosity=2) # 运行测试用例 runner.run(suit) else: self.logger.info("Have no case to test.") except Exception as e: self.logger.error(str(e)) finally: self.logger.info("*********TEST END*********") f.close() if __name__ == '__main__': testRun = RunTest() testRun.run()
起初,不会面向对象的你,只能具体化代码;而后,刚学会面向对象的你,又把所有内容都抽象化,使得自己的架子越来越大,想的东西越来越多,希望一个架子能完成所有的功能。
应该结合具体和抽象共同去完成一个功能,我把这个叫结构化抽象,抽象单一功能后就对应一个实例去测试下。。。。。。
写的有问题的地方大牛们理解下,只是个人观点,欢迎大家指正!!!
我也是新手,在此记录下思路,希望和大家一起学习,