pytest

您所在的位置:网站首页 pytest参数化后如何让用例执行一次 pytest

pytest

2023-07-01 18:16| 来源: 网络整理| 查看: 265

关于分布式插件pytest-xdist进程级并发参数化说明

​ UI自动化脚本耗费时间较长,效率低下,我们该如何处理这种情况,提升测试效率,下面我会就分布式插件pytest-xdist的一个动态参数化问题,简单说明一下pytest-xdist的工作流程,以及数据收集过程,希望对大家有所帮助。

​ –成都-阿木木

说明:

​ 场景:我有100个UI自动化用例,假设每个用例执行时间为一分钟,那么我顺序执行需要执行100分钟,在敏捷开发模式下,耗时比较长,如果赶上项目上线,需要快速得到项目上线后的反馈,那么效率是非常低下的。使用pytest-xdist可以指定多个worker,进行执行用例,我指定10个worker,那么我整个测试脚本执行下来的理论时间为10分钟(PS:只是理论时间),大大的提升了效率。

分布式用例设计原则

用例独立,独立运行用例没有顺序,随机运行用例重复运行,运行结束即清理用例的参数化使用非动态数据(重点:下面讲)

pytest-xdist安装

pip install pytest-xdist

pytest-xdist使用

在pytest的命令行参数中指定-n x(运行的进程数量)

-n auto:可以自动获取系统的CPU核数,启用该参数CPU占用率会非常高,每个进程执行速度会非常慢,所以,worker越多,并不会按照理论时间来进行测试脚本运行效率的提升

-n x:手动指定CPU数量

pytest-xdist分布式执行用例参数化说明

这是一个测试路由器的case,使用了pytest.mark.parametrize进行参数生成,它的作用和ddt数据驱动框架一样,会执行两次测试用例,第一次传递参数“”,第二次传递一个随机10位的字符串参数

@allure.story("Service") @allure.severity("normal") @allure.description("服务模块-路由器列表-新建路由器-序列号") @allure.testcase("XXX", name="测试用例位置") @allure.title("服务模块-路由器列表-新建路由器-序列号") @pytest.mark.parametrize("router_sn", ("", PubMethod.random_string("bnuiowehosdc235342", 10))) def test_617(self, login_page_class, service_page_class, function_driver, router_sn): logging.info("进入测试") login_page_class.mode_login_into_url() service_page_class.click_free_alert_close_icon() service_page_class.click_create_router_btn() service_page_class.send_keys_router_name(PubMethod.random_string("hfbudsio2353256", 10)) service_page_class.send_keys_router_sn(router_sn) service_page_class.click_router_alert_determine_btn() tips_by_router_sn = service_page_class.get_tips_by_router_sn() if len(router_sn) == 0: AssertMethod.assert_equal_screen_shot(function_driver, (tips_by_router_sn, "请输入路由器序列号!")) else: AssertMethod.assert_equal_screen_shot(function_driver, (tips_by_router_sn, "请输入正确的路由器序列号!"))

这是pytest的命令行启动参数,我指定了-n参数为auto,运行run.py,框架路径:https://github.com/chineseluo/ui_auto_frame_v2,可以自己下载测试

test_args = ['-s', '-q', '-n=auto', '--browser={}'.format(browser), '--browser_opt={}'.format(browser_opt), '--type_driver={}'.format(type_driver)] bringing up nodes... [gw0] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw1] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw2] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw3] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw4] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw5] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw6] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw7] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] gw0 [41] / gw1 [41] / gw2 [41] / gw3 [41] / gw4 [41] / gw5 [41] / gw6 [41] / gw7 [41] scheduling tests via LoadScheduling =================================== ERRORS ==================================== ____________________________ ERROR collecting gw1 _____________________________ Different tests were collected between gw0 and gw1. The difference is: --- gw0 +++ gw1 @@ -22,7 +22,7 @@ TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_615 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_616 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_617[] -TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_617[45u333isnn] +TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_617[iw543ui332] TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_618_1 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_618_2 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_618_3 ____________________________ ERROR collecting gw2 _____________________________ Different tests were collected between gw0 and gw2. The difference is: --- gw0 +++ gw2 @@ -22,7 +22,7 @@ TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_615 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_616 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_617[] -TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_617[45u333isnn] +TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_617[is4no2hw55] TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_618_1 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_618_2 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_618_3 ____________________________ ERROR collecting gw3 _____________________________ Different tests were collected between gw0 and gw3. The difference is: --- gw0 +++ gw3

可以看到我一用启用了8个worker,gw0~gw7,读取的系统默认CPU核数为8,gw0~gw1、gw0~gw2、gw0~gw3…以及其他几个worker报错:

Different tests were collected between gw0 and gw1. The difference is:--- gw0 +++ gw1

可以看到在test_617的两条case异常,有一个+号和减号,分析xdist用例收集,进程调度原理: 在这里插入图片描述 收集caseID及进程分发说明:根据简单的示意图可以看出,每一个worker(gwxxx)都会去进行一次标准caseID的收集,整个caseID的收集过程和pytest本身收集caseID的过程是一致的,每一个worker收集完caseID后发送给主节点(管理节点),主节点进行caseID的检查,包括了测试数据的检查,保证每个worker获取的case的数据是一致的,"generating tests twice must produce identical tests" 。在所有worker的测试用例集合相同的前提下,进行测试任务的分发,主节点下发的不再是caseID,而是测试case的索引,以方便告诉每个worker执行哪一个测试用例。 在这里插入图片描述 ​ 那么现在可以知道该异常出现的原因了,也就是说pytest-xdist在进行caseID收集的时候发现,参数化的case的入参不一致,我们是随机生成的,它只支持静态的有序的入参,下面我们修改一下脚本进行验证。

@allure.story("Service") @allure.severity("normal") @allure.description("服务模块-路由器列表-新建路由器-序列号") @allure.testcase("XXX", name="测试用例位置") @allure.title("服务模块-路由器列表-新建路由器-序列号") def test_617_1(self, login_page_class, service_page_class, function_driver): logging.info("进入测试") login_page_class.mode_login_into_url() service_page_class.click_free_alert_close_icon() service_page_class.click_create_router_btn() service_page_class.send_keys_router_name(PubMethod.random_string("hfbudsio2353256", 10)) service_page_class.send_keys_router_sn("") service_page_class.click_router_alert_determine_btn() tips_by_router_sn = service_page_class.get_tips_by_router_sn() AssertMethod.assert_equal_screen_shot(function_driver, (tips_by_router_sn, "请输入路由器序列号!")) @allure.story("Service") @allure.severity("normal") @allure.description("服务模块-路由器列表-新建路由器-序列号") @allure.testcase("XXX", name="测试用例位置") @allure.title("服务模块-路由器列表-新建路由器-序列号") def test_617_2(self, login_page_class, service_page_class, function_driver): logging.info("进入测试") login_page_class.mode_login_into_url() service_page_class.click_free_alert_close_icon() service_page_class.click_create_router_btn() service_page_class.send_keys_router_name(PubMethod.random_string("hfbudsio2353256", 10)) service_page_class.send_keys_router_sn(PubMethod.random_string("bnuiowehosdc235342", 10)) service_page_class.click_router_alert_determine_btn() AssertMethod.assert_equal_screen_shot(function_driver, (tips_by_router_sn, "请输入正确的路由器序列号!"))

检查日志,查看caseID的收集是否异常,可以看到下面的日志中,用例的ID收集无异常,验证通过。

bringing up nodes... [gw0] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw1] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw2] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw3] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw4] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw5] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw6] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] [gw7] Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] gw0 [41] / gw1 [41] / gw2 [41] / gw3 [41] / gw4 [41] / gw5 [41] / gw6 [41] / gw7 [41] scheduling tests via LoadScheduling TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_606_1 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_605_1_and_2[普通用户] TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_606_2 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_606_4 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_606_3 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_606_5 TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_605_1_and_2[管理员] TestCases/Service/test_servicePageCase.py::TestServicePageCase::test_606_6

总结:

​ 通过分析pytest-xdist的参数化异常原因,可以了解到pytest-xdist的工作流程原理,针对性的处理异常,如果需要使用到pytest-xdist插件的小伙伴,可以看一下,做一下测试,进行简单验证加深了解。 欢迎加入测试交流群:夜行者自动化测试(816489363)进行交流学习QAQ



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3