# 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)运行在调用方法的前后 - 好像被弃用了
```

#### @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")
```

> aaa 方法有前置,bbb方法没有前置
使用 yield 实现后置
```python
@pytest.fixture(scope="function")
def fixture_function():
print("\n这是前后置的方法,可以实现部分以及全部用例的前后置")
yield
print("实现后置")
```

加上 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")
```

class 级别

##### 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

> 注:yield 和 return不能同时使用,否则不能取到值
>
> 可以通过 yield 取值
>
> yield request.param
```python
@pytest.fixture(params=['aaa', 'bbb', 'ccc'])
def fixture_function(request):
print("pre")
yield request.param
print("之后")
```
##### ids

修改这个值
```python
@pytest.fixture(params=['aaa', 'bbb', 'ccc'], ids=['a', 'b', 'c'])
def fixture_function(request):
print("pre")
yield request.param
print("之后")
```

##### 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}]
```
-
## POM
全称 page object model 页面对象模型
把项目当中的所有需要自动化测试的页面封装到类里面:一个页面对应一个类。一个类就是一个模型,通过模型生成页面对象
POM 分三层:
- basePage 基础页面层:
- 主要封装一些最基本的selenium的原生的方法
- 定位元素
- 跳转框架
- 处理下拉框等
- PO 页面层
- 页面对象层,把一个一个的页面封装成一个一个的类
- 获取页面元素定位
- 封装页面元素对象
- 页面产生的动作
- 测试用例层
- 测试用例的业务逻辑以及数据驱动
第二层PO层继承第一层基础层,第三层 测试用例层 调用 第二层的方法
## 自动化测试框架
框架
POM
日志处理
全局配置文件 ini yaml
数据驱动
各种封装
