目录
- [一、PyTest 框架](#一、PyTest 框架)
-
- [2. 特点](#2. 特点)
- [2. 安装步骤](#2. 安装步骤)
- [3. 基本使⽤](#3. 基本使⽤)
-
- [3.1 测试函数形式](#3.1 测试函数形式)
- [3.2 执⾏⽅式](#3.2 执⾏⽅式)
- [3.3 测试类形式](#3.3 测试类形式)
- [3.4 执⾏⽅式](#3.4 执⾏⽅式)
- [3.5 另⼀种执⾏⽅式: 主函数执⾏](#3.5 另⼀种执⾏⽅式: 主函数执⾏)
- [3.6 特殊⽅法: 函数级别](#3.6 特殊⽅法: 函数级别)
- [3.7 特殊⽅法: 类级别](#3.7 特殊⽅法: 类级别)
- [3.8 特殊⽅法: 函数级别和类级别同时使⽤](#3.8 特殊⽅法: 函数级别和类级别同时使⽤)
- [4. pytest 配置⽂件](#4. pytest 配置⽂件)
-
- [4.1 选项字段获取](#4.1 选项字段获取)
- [4.2 编写步骤](#4.2 编写步骤)
- [4.3 使⽤步骤](#4.3 使⽤步骤)
- [4.4 默认设置](#4.4 默认设置)
- [4.5 ⾃定义规则](#4.5 ⾃定义规则)
- [4.6 扩展: 指定单个⽂件/类/⽅法执⾏](#4.6 扩展: 指定单个⽂件/类/⽅法执⾏)
- [二、 pytest 常⽤插件](#二、 pytest 常⽤插件)
-
- [1. HTML 报告插件](#1. HTML 报告插件)
-
- [1.1 安装步骤](#1.1 安装步骤)
- [1.2 使⽤步骤](#1.2 使⽤步骤)
- [2. 控制⽅法执⾏顺序插件](#2. 控制⽅法执⾏顺序插件)
-
- [2.1 安装步骤](#2.1 安装步骤)
- [2.2 使⽤步骤](#2.2 使⽤步骤)
- [3. 失败重试插件](#3. 失败重试插件)
-
- [3.1 安装步骤](#3.1 安装步骤)
- [3.2 使⽤步骤](#3.2 使⽤步骤)
- [三、pytest ⾼级功能](#三、pytest ⾼级功能)
-
- [1. 跳过操作](#1. 跳过操作)
- [2. 参数化操作](#2. 参数化操作)
-
- [2.1 单个参数](#2.1 单个参数)
- [2.2 多个参数](#2.2 多个参数)
- [3. 扩展: 断⾔⽅法的使⽤](#3. 扩展: 断⾔⽅法的使⽤)
-
- [3.1 Python ⾃带的断⾔⽅法](#3.1 Python ⾃带的断⾔⽅法)
- [3.2 pytest 框架中使⽤断⾔](#3.2 pytest 框架中使⽤断⾔)
- [四、PO 设计模式](#四、PO 设计模式)
-
- [1. v0 版本](#1. v0 版本)
- [2. 账户不存在脚本](#2. 账户不存在脚本)
- [3. 密码错误脚本](#3. 密码错误脚本)
一、PyTest 框架
- 说明: pytest 是 Python 的⼀种单元测试框架,同⾃带的 UnitTest 测试框架类似,相⽐ UnitTest 框架使⽤起来更简洁,效率更⾼。(在⾃动化测试中同样充当测试执⾏的功能! 并且可以跟 UnitTest 互换)
2. 特点
-
- ⾮常容易上⼿,⼊⻔简单,⽂档丰富,⽂档中有很多实例可以参考
- 官⽅⽂档地址: https://docs.pytest.org/en/6.2.x/
-
- ⽀持简单的单元测试和复杂的功能测试
-
- ⽀持参数化: UnitTest 需要通过插件扩展参数化功能!
-
- 执⾏测试过程中可以将某些测试跳过,或者对某些预期失败的 Case 标记成失败
-
- ⽀持重复执⾏失败的 Case : 通过安装插件实现
-
- ⽀持运⾏由 Nose,UnitTest 编写的测试 Case : pytest 框架的脚本在 UnitTest 下⽆法执⾏
-
- 具有很多第三⽅插件,并且可以⾃定义扩展
- 插件获取⽹站:
-
- ⽅便的和持续集成⼯具集成
2. 安装步骤
说明: 与 UnitTest 不同的是, pytest 框架需要先安装才能使⽤!
- 安装:
- pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pytest
- 查看: pip show pytest
- 确认版本: pytest --version
安装与查看版本步骤

3. 基本使⽤
3.1 测试函数形式
python
# 函数形式
def test_func(): # 要求函数名以 test 开头
"""测试函数"""
print('我是测试函数')
3.2 执⾏⽅式

3.3 测试类形式
python
# 测试类形式
class TestDemo(object): # 正常定义类, 但是测试类名必须以 Test 开头
"""测试示例类"""
def test_method1(self): # 正常定义⽅法, 但是测试⽅法名必须以
test 开头
"""测试⽅法1"""
print('测试⽅法1')
def test_method2(self):
"""测试⽅法2"""
print('测试⽅法2')
3.4 执⾏⽅式

3.5 另⼀种执⾏⽅式: 主函数执⾏
说明: 使⽤主函数, 可以实现在代码⽂件内, ⿏标右键菜单执⾏
- 测试函数形式下使⽤
python
# 测试函数形式
import pytest
def test_func(): # 要求函数名以 test 开头
"""测试函数"""
print('我是测试函数')
if __name__ == '__main__':
# 语法: pytest.main(['-s', '⽂件名.py'])
pytest.main(['-s', 'hm03_pytest_basic_03.py'])
- 测试类形式下使⽤
python
# 测试类形式
import pytest
class TestDemo(object): # 正常定义类, 但是测试类名必须以 Test 开头
"""测试示例类"""
def test_method1(self): # 正常定义⽅法, 但是测试⽅法名必须以 test 开头
"""测试⽅法1"""
print('测试⽅法1')
def test_method2(self):
"""测试⽅法2"""
print('测试⽅法2')
if __name__ == '__main__':
# 语法: pytest.main(['-s', '⽂件名.py'])
pytest.main(['-s', 'hm04_pytest_basic_04.py'])
3.6 特殊⽅法: 函数级别
- 说明: 函数级别特殊⽅法的执⾏逻辑与 UnitTest 中的 Fixture 的⽅法级别的执⾏逻辑⼀致!
python
"""
特殊⽅法: 函数级别
"""
import pytest
class TestDemo(object):
"""测试示例类"""
# 说明: 特殊⽅法名写法固定, 没有代码提示, 需要⼿写!
# 注意: 函数级别执⾏顺序:
# 先 setup() -> 测试⽅法1 -> teardown() ⽅法, 再 setup() -> 测试⽅法2 -> teardown() ⽅法
def setup(self):
"""开始⽅法"""
print('函数 -> 开始')
def teardown(self):
"""结束⽅法"""
print('函数 -> 结束')
def test_method1(self):
"""示例测试⽅法"""
print('测试⽅法1')
def test_method2(self):
"""示例测试⽅法"""
print('测试⽅法2')
if __name__ == '__main__':
pytest.main(['-s', 'hm05_pytest_basic_05.py'])
3.7 特殊⽅法: 类级别
- 说明: 类级别特殊⽅法的执⾏逻辑与 UnitTest 中的 Fixture 的类级别的执⾏逻辑⼀致!
python
"""
特殊⽅法: 类级别
"""
import pytest
class TestDemo(object):
"""测试示例类"""
# 说明: 特殊⽅法名写法固定, 没有代码提示, 需要⼿写!
# 注意: 类级别执⾏顺序:
# 先 setup_class() -> 测试⽅法1 -> 测试⽅法2 ->
teardown_class() ⽅法
def setup_class(self):
"""开始⽅法"""
print('类 -> 开始')
def teardown_class(self):
"""结束⽅法"""
print('类 -> 结束')
def test_method1(self):
"""示例测试⽅法"""
print('测试⽅法1')
def test_method2(self):
"""示例测试⽅法"""
print('测试⽅法2')
if __name__ == '__main__':
pytest.main(['-s', 'hm06_pytest_basic_06.py'])
3.8 特殊⽅法: 函数级别和类级别同时使⽤
python
"""
特殊⽅法: 函数级别和类级别同时使⽤
"""
import pytest
class TestDemo(object):
"""示例测试类"""
# 执⾏顺序: 1 -> 3 -> 5 -> 4 -> 3-> 6 -> 4 -> 2
def setup_class(self): # 1
print('类级别 ->> 开始')
def teardown_class(self): # 2
print('类级别 ->> 结束')
def setup(self): # 3
print('函数级别 -> 开始')
def teardown(self): # 4
print('函数级别 -> 结束')
def test_method1(self): # 5
"""测试⽅法1"""
print('测试⽅法1')
def test_method2(self): # 6
"""测试⽅法2"""
print('测试⽅法2')
if __name__ == '__main__':
pytest.main(['-s', 'hm07_pytest_basic_07.py'])
4. pytest 配置⽂件
- 说明: 使⽤配置⽂件后可以快速的使⽤配置的项来选择执⾏哪些测试模块
- 注意:
*- 在 Windows 系统下, pytest 配置⽂件中, 不允许写注释信息
-
- ⼀个⼯程内只需要⼀个 pytest 配置⽂件, 并且需要保证⽂件名正确
-
- ⼀般情况, 只需要将 pytest 配置⽂件, 置于⼯程根⽬录下
-
- 配置有 pytest 配置⽂件的⼯程, 只需要打开命令⾏, 输⼊ pytest 指令, 即可执⾏测试
4.1 选项字段获取

4.2 编写步骤

4.3 使⽤步骤
- 说明: 打开命令⾏ -> 输⼊ pytest -> 执⾏即可
4.4 默认设置
- 说明: 测试⽤例⽂件名/测试类名/测试⽅法名均为 Test/test 开头
pytest
testpaths = ./case
addopts = -s
python_files = test*.py
python_classes = Test*
python_functions = test*
4.5 ⾃定义规则
- 说明: 测试⽤例⽂件名/测试类名/测试⽅法名, 需要根据具体项⽬进⾏设置, 以下以 Hm/hm为例
pytest
testpaths = ./case
addopts = -s
python_files = hm*.py
python_classes = Hm*
python_functions = hm*
4.6 扩展: 指定单个⽂件/类/⽅法执⾏
pytest
testpaths = ./case
addopts = -s
python_files = demo_case1.py
python_classes = DemoDemo1
python_functions = demo_method1
二、 pytest 常⽤插件
1. HTML 报告插件
1.1 安装步骤
- ⽅式1 -> 安装: pip install pytest-html
- ⽅式2 -> PyCharm 中安装

1.2 使⽤步骤
-
- 在 pytest 配置⽂件中, 增加命令选项
- 选项: --html=./报告路径/报告⽂件名.html --self-contained-html
- 说明: --self-contained-html 将 CSS ⽂件内嵌到报告⽂件中
-
- 执⾏ pytest 指令, 运⾏测试即可
-
添加命令选项执⾏
-
查看⽣成的报告内容
2. 控制⽅法执⾏顺序插件
2.1 安装步骤
- ⽅式1 -> 安装: pip install pytest-ordering
- ⽅式2 -> PyCharm 中安装

2.2 使⽤步骤
- 说明: 直接在测试类或测试⽅法上⽅添加 @pytest.mark.run(order=序号)
python
"""
pytest 控制⽅法执⾏顺序插件
"""
import pytest
@pytest.mark.run(order=2)
class TestDemo(object):
"""示例测试类"""
# 语法: @pytest.mark.run(order=序号)
# 注意: run(order=序号) 没有代码提示, 需要⼿写!
# @pytest.mark.run(order=-3)
def test_method1(self):
"""测试⽅法1"""
print('测试⽅法1')
# @pytest.mark.run(order=1)
def test_method2(self):
"""测试⽅法2"""
print('测试⽅法2')
# @pytest.mark.run(order=2)
def test_method3(self):
"""测试⽅法3"""
print('测试⽅法3')
# 扩展: 序号⽀持正数和负数, 以及正负混合
# 1. 纯正数: 数越⼩, 优先级越⾼[掌握]
# 2. 纯负数: 数越⼩, 优先级越⾼[了解]
# 3. 正负混合: 正数先按照顺序执⾏, 负数最后执⾏[了解]
# 注意: 控制⽅法执⾏顺序对测试类同样有效!
@pytest.mark.run(order=1)
class TestDemo2(object):
"""测试类2"""
def test_method(self):
print('测试类2 -> 测试⽅法')
3. 失败重试插件
3.1 安装步骤
- ⽅式1 -> 安装: pip install pytest-rerunfailures
- ⽅式2 -> PyCharm 中安装

3.2 使⽤步骤
-
- 在 pytest 配置⽂件中, 增加命令选项
- 选项: --reruns 次数
-
- 执⾏ pytest 指令, 运⾏测试即可
- 执⾏ pytest 指令, 运⾏测试即可
三、pytest ⾼级功能
1. 跳过操作
- 说明: 对于完成的代码或版本不对应的代码, 可以设置跳过使之不参与测试执⾏
python
"""
跳过功能
"""
import pytest
version = 25 # 模拟软件版本号变量
class TestDemo1(object):
"""示例测试类"""
def test_method1(self):
"""测试⽅法"""
print('测试⽅法1')
# 语法: @pytest.mark.skipif(符合的条件, reason='跳过的原因')
# 说明: 如果满⾜条件, 以下⽅法或测试类执⾏跳过, 不执⾏!
# 注意: reason= 不能省略, 否则报错!
# @pytest.mark.skipif(version >= 25, 'xxx') # 错误样例
@pytest.mark.skipif(version >= 25, reason='当前版本不执⾏') #
正确样例
def test_method2(self):
"""测试⽅法"""
print('测试⽅法2')
# 说明: 同样可以跳过测试类
@pytest.mark.skipif(version >= 25, reason='当前版本不执⾏')
class TestDemo2(object):
"""示例测试类2"""
def test_method(self):
print('测试类2 -> 测试⽅法')
if __name__ == '__main__':
pytest.main(['-s', 'hm10_pytest_skip.py'])
2. 参数化操作
- 说明: 与 UnitTest 框架不同, pytest 框架⾃带参数化功能, 调⽤对应⽅法并传⼊数据, 即可完成参数化实现
2.1 单个参数
python
"""
pytest 参数化功能: 单个参数
"""
import pytest
class TestDemo(object):
"""示例测试类"""
# 语法: @pytest.mark.parametrize('参数变量', ['数值1', '数值
2', ...])
@pytest.mark.parametrize('name', ['⼩明', '⼩刚', '⼩红'])
def test_method(self, name):
"""测试⽅法"""
print('获取的名字是:', name)
if __name__ == '__main__':
pytest.main(['-s', 'hm11_pytest_para1.py'])
2.2 多个参数
python
"""
pytest 参数化: 多个参数
"""
import pytest
class TestDemo(object):
"""示例测试类"""
# 语法: @pytest.mark.parametrize('参数1, 参数n', [(数据1-1, 数
据1-2), (数据2-1, 数据2-2), ...])
# 注意:
# 1. 多个参数必须置于同⼀个字符串内!
# 2. 数据格式必须是: [(), ()] 或者 [[], []]
# 扩展: 另⼀种写法
# @pytest.mark.parametrize(('name', 'pwd'), [('admin',
123456), ('test', 654321), ('xxx', 'yyy')])
@pytest.mark.parametrize('name, pwd', [('admin', 123456),
('test', 654321), ('xxx', 'yyy')])
def test_method(self, name, pwd):
"""测试⽅法"""
print('账号:{} 密码:{}'.format(name, pwd))
if __name__ == '__main__':
pytest.main(['-s', 'hm12_pytest_para2.py'])
3. 扩展: 断⾔⽅法的使⽤
- 说明: 与 UnitTest 框架不同的是, pytest 框架使⽤ Python ⾃带的断⾔⽅法实现断⾔操作的
3.1 Python ⾃带的断⾔⽅法
python
# 语法: assert 表达式
# 说明: 断⾔后接的表达式的结果为 Ture(断⾔通过), 为 False(断⾔失败)
# 预期相等:
assert 1 == 1
# 预期包含:
assert 'admin' in '欢迎 admin 归来!'
3.2 pytest 框架中使⽤断⾔
python
"""
pytest 断⾔
"""
import pytest
def add_func(num1, num2):
"""加法函数"""
return num1 + num2
class TestDemo(object):
"""示例测试类"""
def test_method(self):
"""加法测试⽅法"""
# 调⽤被测函数
result = add_func(1, 2)
# 断⾔判断结果
assert 4 == result
if __name__ == '__main__':
pytest.main(['-s', 'hm14_pytest_assert.py'])
四、PO 设计模式
1. v0 版本
- 特点: 对于同⼀个待测模块, 对应的测试代码步骤完全⼀致, 只有测试数据存在差异, 因此对于反复出现的相同逻辑的测试代码, 需要考虑通过封装的思路对⻬进⾏整理, 以提⾼测试脚本的编写效率.
- 注意: 通过直接定位元素并操作元素的⽅式实现的⾃动化脚本, 已经算是 UI ⾃动化的实现形式了!
2. 账户不存在脚本
python
"""
账号不存在测试⽤例
"""
from time import sleep
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('http://127.0.0.1/')
driver.maximize_window() # 窗⼝最⼤化
driver.implicitly_wait(10) # 隐式等待
# 1. 点击⾸⻚的'登录'链接,进⼊登录⻚⾯
driver.find_element_by_link_text('登录').click()
# 2. 输⼊⼀个不存在的⽤户名
driver.find_element_by_id('username').send_keys('13811110001')
# 3. 输⼊密码
driver.find_element_by_id('password').send_keys('123456')
# 4. 输⼊验证码
driver.find_element_by_id('verify_code').send_keys('8888')
# 5. 点击登录按钮
driver.find_element_by_name('sbtbutton').click()
# 6. 获取错误提示信息
# 获取元素⽂本值: 元素对象.text
msg = driver.find_element_by_class_name('layui-layercontent').text
print('错误信息为:',
msg)
sleep(3)
driver.quit()
3. 密码错误脚本
python
"""
密码错误测试⽤例
"""
from time import sleep
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('http://127.0.0.1/')
driver.maximize_window() # 窗⼝最⼤化
driver.implicitly_wait(10) # 隐式等待
# 1. 点击⾸⻚的'登录'链接,进⼊登录⻚⾯
driver.find_element_by_link_text('登录').click()
# 2. 输⼊⽤户名
driver.find_element_by_id('username').send_keys('13800001111')
# 3. 输⼊错误密码
driver.find_element_by_id('password').send_keys('error')
# 4. 输⼊验证码
driver.find_element_by_id('verify_code').send_keys('8888')
# 5. 点击登录按钮
driver.find_element_by_name('sbtbutton').click()
# 6. 获取错误提示信息
# 获取元素⽂本值: 元素对象.text
msg = driver.find_element_by_class_name('layui-layercontent').text
print('错误信息为:',
msg)
sleep(3)
driver.quit()