事件循环负责执行协程并返回结果,在最后的结果收集中,我用测试用例目录来对结果进行了分类,这为接下来的自动生成pytest认可的测试用例打下了良好的基础
def main(test_cases): """ 事件循环主函数,负责所有接口请求的执行 :param test_cases: :return: """ loop = asyncio.get_event_loop() semaphore = asyncio.Semaphore(bxmat.semaphore) # 需要处理的任务 # tasks = [asyncio.ensure_future(one(case_name=test_case, semaphore=semaphore)) for test_case in test_cases] task = loop.create_task(entrace(test_cases, loop, semaphore)) # 将协程注册到事件循环,并启动事件循环 try: # loop.run_until_complete(asyncio.gather(*tasks)) loop.run_until_complete(task) finally: loop.close() return task.result()
第二部分动态生成pytest认可的测试用例首先说明下pytest的运行机制,pytest首先会在当前目录下找conftest.py文件,如果找到了,则先运行它,然后根据命令行参数去指定的目录下找test开头或结尾的.py文件,如果找到了,如果找到了,再分析fixture,如果有session或module类型的,并且参数autotest=True或标记了pytest.mark.usefixtures(a…),则先运行它们;再去依次找类、方法等,规则类似 。大概就是这样一个过程 。
可以看出,pytest测试运行起来的关键是,必须有至少一个被pytest发现机制认可的 testxx.py 文件,文件中有 TestxxClass 类,类中至少有一个 def testxx(self) 方法 。
现在并没有任何pytest认可的测试文件,所以我的想法是先创建一个引导型的测试文件,它负责让pytest动起来 。可以用 pytest.skip() 让其中的测试方法跳过 。然后我们的目标是在pytest动起来之后,怎么动态生成用例,然后发现这些用例,执行这些用例,生成测试报告,一气呵成 。
# test_bootstrap.py import pytest class TestStarter(object): def test_start(self): pytest.skip('此为测试启动方法, 不执行')
我想到的是通过fixture,因为fixture有setup的能力,这样我通过定义一个scope为session的fixture,然后在TestStarter上面标记use,就可以在导入TestStarter之前预先处理一些事情,那么我把生成用例的操作放在这个fixture里就能完成目标了 。
# test_bootstrap.py import pytest @pytest.mark.usefixtures('te', 'test_cases') class TestStarter(object): def test_start(self): pytest.skip('此为测试启动方法, 不执行')
pytest有个 --rootdir 参数,该fixture的核心目的就是,通过 --rootdir 获取到目标目录,找出里面的 .yml 测试文件,运行后获得测试数据,然后为每个目录创建一份 testxx.py 的测试文件,文件内容就是 content 变量的内容,然后把这些参数再传给 pytest.main() 方法执行测试用例的测试,也就是在pytest内部再运行了一个pytest!最后把生成的测试文件删除 。注意该fixture要定义在 conftest.py 里面,因为pytest对于 conftest 中定义的内容有自发现能力,不需要额外导入 。
# conftest.py @pytest.fixture(scope='session') def test_cases(request): """ 测试用例生成处理 :param request: :return: """ var = request.config.getoption("--rootdir") test_file = request.config.getoption("--tf") env = request.config.getoption("--te") cases = [] if test_file: cases = [test_file] else: if os.path.isdir(var): for root, dirs, files in os.walk(var): if re.match(r'w+', root): if files: cases.extend([os.path.join(root, file) for file in files if file.endswith('yml')]) data = https://www.isolves.com/it/cxkf/kj/2021-04-01/main(cases) content = """ import allure from conftest import CaseMetaClass @allure.feature('{}接口测试({}项目)') class Test{}API(object, metaclass=CaseMetaClass): test_cases_data = {} """ test_cases_files = [] if os.path.isdir(var): for root, dirs, files in os.walk(var): if not ('.' in root or '__' in root): if files: case_name = os.path.basename(root) project_name = os.path.basename(os.path.dirname(root)) test_case_file = os.path.join(root, 'test_{}.py'.format(case_name)) with open(test_case_file, 'w', encoding='utf-8') as fw: fw.write(content.format(case_name, project_name, case_name.title(), data.get(root))) test_cases_files.append(test_case_file) if test_file: temp = os.path.dirname(test_file) py_file = os.path.join(temp, 'test_{}.py'.format(os.path.basename(temp))) else: py_file = var pytest.main([ '-v', py_file, '--alluredir', 'report', '--te', env, '--capture', 'no', '--disable-warnings', ]) for file in test_cases_files: os.remove(file) return test_cases_files
可以看到,测试文件中有一个 TestxxAPI 的类,它只有一个 test_cases_data 属性,并没有 testxx 方法,所以还不是被pytest认可的测试用例,根本运行不起来 。那么它是怎么解决这个问题的呢?答案就是 CaseMetaClass。
推荐阅读
- 八种实用的免费游戏开发软件工具
- 苹果|苹果商店将下架长期不更新App 开发者愤怒抗议:官方解释背后原因
- 带你玩转JSP网站开发技术
- 怎么开发直播APP?资深技术划重点了
- Jenkins与Docker的自动化CI/CD流水线实战
- SpringBoot配置文件数据加密自定义开发详解
- 实战Redis,解决高并发性能问题
- 微服务版 前后端分离开源快速开发平台
- 在java的实际开发中,何时用传统IO,何时用NIO?
- Vue前端开发规范
