# selenium+pytest **Repository Path**: anonymous-bai/selenium-pytest ## Basic Information - **Project Name**: selenium+pytest - **Description**: 用selenium + pytest 框架做一个简单的自动化测试 - **Primary Language**: Unknown - **License**: AFL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2024-09-16 - **Last Updated**: 2025-05-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 安装插件 > pytest-xdist : 多线程执行 > > pytest-rerunfailures : 失败重跑 > > pytest-html:html 测试报告 > > allure-pytest : allure 测试报告 > > pytest-ordering : 测试执行顺序 # 介绍 自动化测试框架 - 目前只有 pytest 做的接口自动化测试框架 1、pytest 数据驱动(参数化) 2、对 【selenium】 二次封装 3、对 【log】 二次封装 4、对 【sql数据库】二次封装 5、po分层实现 ui 自动化 6、conftest.py 前后置处理 7、测试用例失败截图 8、allure 测试报告 # 被测系统 图书商城项目,简单做一下自动化测试的脚本 # 文件介绍 ## pytest.ini pytest.ini pytest全局配置文件,是pytest的测试框架的核心配置文件 1、一般放在跟目录下 2、编码:必须是ANSI,可以使用 notpad++ 修改编码格式 3.作用:改变pytest的默认的行为 4、运行的规则:不管是主函数的模式运行,命令行的模式运行,都会去读取这个文件 ```python [pytest] # 命令行的参数,用空格分隔 addopts = -vs # 测试用例的路径 testpaths = ./testCases # 模块名的规则 python_files = test_*.py # 类名规则 python_classes = Test* # 方法名的规则 python_functions = test* markers = smoke:冒烟用例 usermanage:用户管理模块 productmanage:商品管理模块 ``` > 命令以及插件的简单介绍 ```python # -s 表示输出调试信息,包括print打印的信息 # -v 显示更详细的信息 # -vs 两个参数可以一起用 # "-n == 2", 多线程 或者 分布式运行测试用例 需要安装插件 pytest-xdist # --reruns NUM 失败用例重跑 需要安装插件 pytest-rerunfailures # -x 表示只要有一个测试用例报错,就停止测试 # --maxfail = 2 出现两个用例失败就停止 # -k 根据测试用例部分字符串指定测试用例, 如pytest -vs ./testCases/ -k "ao" # --html ./report/report.html 生成html的测试报告, 需要安装插件 pytest-html # 生成美观的测试报告 allure-pytest # pytest-ordering 用于改变测试用例的执行顺序 ``` ## pytest ### pytest 执行顺序 - unittest:按照ascII的大小来绝对的执行 - pytest:默认从上到下 **需要安装 pytest-ordering 插件,用于改变用例的执行顺序** - 改变默认的执行顺序:使用mark标记 ```python @pytest.mark.run(order=3) ``` ### pytest 分组执行 smoke : 冒烟测试,分布在各个模块里面 pytest -m "smoke" ### pytest 跳过用例 - 无条件跳过 `@pytest.mark.skip(reason="aaa")` - 有条件跳过 `@pytest.mark.skipif(age>=18, reason="bb")` ### pytest 前后置处理 pytest 框架实现一些 **前后置(固件、夹具)**的处理,常用的三种 #### setup_XXX / teardown_XXX ```python # 在每个用例之前后都会执行一次 def setup_method(self): print("在每个 类中的方法执行 之前的工作,比如,打开浏览器、加载网页") def teardown_method(self): print("在每个 类中的方法执行 之后的工作,比如,关闭浏览器") # 在每个类的前后执行一次 def setup_class(self): print("在每个 类开始执行之前 之前的工作,比如,创建日志对象,创建数据库的连接,创建接口的请求对象") def teardown_class(self): print("在每个 类执行 之后的扫尾工作,比如,销毁日志队形,销毁数据库的连接,销毁接口的请求对象") ``` 为什么需要这些功能 web制动化执行之前,可能需要打开浏览器的操作,执行完成之后,可能会有关闭浏览器的操作 可以在测试的类里面写 setup/teardown 被弃用了 > **版本更迭,规则变动** > > 在pytest早期的版本里是支持直接使用`setup`和`teardown`来完成对于测试用例的前后置处理的,这是因为当时基于人们对于unittest的使用习惯,所以pytest支持了这样的写法。 > > 但是长期维护这个写法,效率太低,所以在数次版本更新后,pytest就不再支持单纯的`setup`和`teardown`的写法。 ```python 1. 模块级别(setup_module / teardown_module)模块始末,全局的(优先最高) - 生效 2. 函数级(setup_function / teardown_function)只对函数用例生效 (不在类中)- 生效 在类外面的叫函数,在类里面的叫方法 3. 类级 (setup_class / teardown_class)只在类中前后运行一次 (在类中) - 生效 4. 方法级 (setup_method / teardown_method)开始于方法始末 (在类中) - 生效 5. 类里面 (setup / teardown)运行在调用方法的前后 - 好像被弃用了 ``` ![image-20240923145211909](./README.assets/image-20240923145211909.png) #### @pytest.fixture ##### scope autouse 使用@pytest.fixture()装饰器可以实现部分或者全部用例的前后置操作 ```python import pytest """ scope 表示被 @pytest.fixture 标记的方法的作用域,function(默认)、class、module、package/session params: 参数化,支持list[]、tuple()、字典列表[{}]、字典元组[{},{},{}] autouse = True: 自动执行,默认是False ids : 当使用 params 参数化时,给每一个值设置一个变量名 name : 表示被 @pytest.fixture 标记的方法取一个别名 """ @pytest.fixture(scope="function") def fixture_function(): print("\n这是前后置的方法,可以实现部分以及全部用例的前后置") class Test_fixture: def test_aaa(self, fixture_function): print("aaa \n test fixture_function") def test_bbb(self): print("bbb") ``` ![image-20240923152046587](./README.assets/image-20240923152046587.png) > aaa 方法有前置,bbb方法没有前置 使用 yield 实现后置 ```python @pytest.fixture(scope="function") def fixture_function(): print("\n这是前后置的方法,可以实现部分以及全部用例的前后置") yield print("实现后置") ``` ![image-20240923152307619](./README.assets/image-20240923152307619.png) 加上 autouse=True 之后就可以自动识别并执行了,不需要手动添加(但是手动添加可以实现部分用例的执行) ```python import pytest """ scope 表示被 @pytest.fixture 标记的方法的作用域,function(默认)、class、module、package/session params: 参数化 autouse = True: 自动执行,默认是False ids : 当使用params参数化时,给每一个值设置一个变量名 name : 表示被 @pytest.fixture 标记的方法取一个别名 """ @pytest.fixture(scope="function", autouse=True) def fixture_function(): print("\n这是前后置的方法,可以实现部分以及全部用例的前后置") yield print("实现后置") class Test_fixture: def test_aaa(self): print("\naaa \n test fixture_function") def test_bbb(self): print("\nbbb") ``` ![image-20240923152544867](./README.assets/image-20240923152544867.png) class 级别 ![image-20240923152746436](./README.assets/image-20240923152746436.png) ##### params ```python import pytest @pytest.fixture(params=['aaa', 'bbb', 'ccc']) def fixture_function(request): return request.param class Test_fixture: def test_aaa(self, fixture_function): print(fixture_function) # 输出的是None def test_bbb(self): print("\nbbb") ``` > 如果写成 > > @pytest.fixture(params=['aaa', 'bbb', 'ccc']) > def fixture_function(): > > > 这样输出的是 None ![image-20240923154434568](README.assets/image-20240923154434568.png) > 注:yield 和 return不能同时使用,否则不能取到值 > > 可以通过 yield 取值 > > yield request.param ```python @pytest.fixture(params=['aaa', 'bbb', 'ccc']) def fixture_function(request): print("pre") yield request.param print("之后") ``` ##### ids ![image-20240923161041032](./README.assets/image-20240923161041032.png) 修改这个值 ```python @pytest.fixture(params=['aaa', 'bbb', 'ccc'], ids=['a', 'b', 'c']) def fixture_function(request): print("pre") yield request.param print("之后") ``` ![image-20240923161202929](./README.assets/image-20240923161202929.png) ##### name > 用完 name 之后,原来的名字就不能用了 ```python import pytest @pytest.fixture(params=['aaa', 'bbb', 'ccc'], ids=['a', 'b', 'c'], name="test_fixture") def fixture_function(request): print("pre") yield request.param print("之后") class Test_fixture: def test_aaa(self, test_fixture): print(test_fixture) def test_bbb(self): print("\nbbb") ``` #### 通过 conftest.py 和 @pytest.fixture()结合使用实现全局前后置的应用 > 项目的全局登录、模块的全局处理 - conftest.py 文件是单独存放的一个夹具配置文件、名字是不能更改的(在用例的同层级里面) - 用处:可以在不同的py文件中使用同一个 fixture - 原则上 conftest.py 需要和运行的用例放到同一层,并且不需要任何的import ### 断言 assert 1==2 ### pytest 结合 allure 生成 allure 测试报告 > 安装插件:pip install allure-pytest > > 下载 allure :https://github.com/allure-framework/allure2/releases > > 解压allure: > > 配置环境变量:安装目录/bin > > 查看配置是否成功 allure --version dos 可以验证,但是pycharm验证失败,重启pycharm ​ 刚配置完,pycharm可能读不到刚配置的环境变量,所以需要重启一下 - 生成接送格式的临时报告 `addopts = -vs --alluredir ./temp` - 生成 allure 报告 `os.system('allure generate ./temp -o ./reports --clean')` - `allure generate` 生成allure报告的固定写法 - `./temp` 找到 `./temp` 临时文件夹 - `-o ./reports` -o是输出的意思,输出到report里面 - `--clean` 清空原来的 reports的内容 ```python if __name__ == '__main__': pytest.main() os.system('allure generate ./temp -o ./reports --clean') ``` ### pytest.mark.parameterize() 参数化 #### 第一种方式 ```python @pytest.mark.parametrize("参数名称", 参数值) def testLogin(self, method, customer_data): req = request(method=method.POST, url="http://localhost:8080/customerLogin", json=customer_data) assert json.loads(req.text)["flag"] is True ``` - 参数名: - 参数值:(列表、元组、字典列表、字典元组),有多个值就会执行多少次用例 #### 第二种方式: 跟unittest 的 ddt 里面的 @unpack解包是一样的 ```python @pytest.mark.parametrize("name, age", [["zhangsan", 13], ["lisi", "15"]]) def test_parameterize_mark(self, name, age): print(name, age) ``` ### yaml 文件详解 -- 实现接口自动化 - 全局的配置文件 ini ,yaml - 用于写测试用例(接口测试用例) yaml简介: 验证 yaml 格式是否正确的网站:http://bejson.com/validators/yaml_editor - 是一种数据格式,支持注释、换行、多行字符串、裸字符串(整型、字符串) - 语法规则 - 区分大小写的 - 使用缩进来表示层级,缩进不能使用 tab键,只能使用 space 键 - 缩进是没有数量的,只要前面是对齐的就可以 - 注释是 # - 数据组成 - map对象,键值对 `key: value` : 后面必须有空格 - ```yaml test_data: name: "aaa" age: 13 test_data1: {name: "bbb", age: 14} ``` - 数组(list),用一组横线开头, - ```yaml test_list: - name: "c" - age: 12 test_list1: [{name: "d"}, {age: 13}] ``` - image-20240924210607510 ## POM 全称 page object model 页面对象模型 把项目当中的所有需要自动化测试的页面封装到类里面:一个页面对应一个类。一个类就是一个模型,通过模型生成页面对象 POM 分三层: - basePage 基础页面层: - 主要封装一些最基本的selenium的原生的方法 - 定位元素 - 跳转框架 - 处理下拉框等 - PO 页面层 - 页面对象层,把一个一个的页面封装成一个一个的类 - 获取页面元素定位 - 封装页面元素对象 - 页面产生的动作 - 测试用例层 - 测试用例的业务逻辑以及数据驱动 第二层PO层继承第一层基础层,第三层 测试用例层 调用 第二层的方法 ## 自动化测试框架 框架 POM 日志处理 全局配置文件 ini yaml 数据驱动 各种封装 image-20240924221914346