测试学习记录,仅供参考!
Pytest 框架
本文实际结合测试项目接口服务进行 API 接口自动化测试;
建议可参考学习:从 0 到 1 搭建完整 Python 语言 Web UI自动化测试学习系列 17--测试框架Pytest基础 1--介绍使用
五、断言
断言是在做自动化测试中很重要的一个概念,通过断言去判定测试结果是通过还是失败,通过断言的方式去校验测试结果。Pytest 框架使用内置断言"assert"语句来进行测试,除了基本的"assert"语句外,还提供了一些丰富的断言工具。
1、在项目根目录 testcases 软件包登录模块login目录下新建名称为test_assert_result.py的 Python 文件,并输入以下内容;
# 导包
import pytest
# 创建一个类--写了一个测试类
class TestAssertResult:
# 相等断言,判断两个值是否相等--断言最常见的一种方式
def test_assert_eq(self):
assert 1 == 1, '登录失败,它们的值不相等'
def test_assert_eq_02(self):
exp = {'msg': '登录成功'}
response = {'msg': '登录成功'}
assert exp == response, '登录失败,它们不相等!'
if __name__ == '__main__':
pytest.main(['-vs', '-k', 'test_assert_result'])
直接使用关键字"assert"断言,后面跟上第一个参数"表达式",第二个参数是可选参数,当断言出现错误时,打印出第二个参数中的消息;
其他断言方式
2、修改test_assert_result.py文件内容;
# 导包
import pytest
import random
# 创建一个类--写了一个测试类
class TestAssertResult:
# 相等断言,判断两个值是否相等--断言最常见的一种方式
def test_assert_eq(self):
assert 1 == 1, '登录失败,它们的值不相等'
def test_assert_eq_02(self):
# 这是一个 预期结果
exp = {'msg': '登录成功'}
# 这是一个 接口实际返回结果
response = {'msg': '登录成功'}
assert exp == response, '登录失败,它们不相等!'
# 不相等断言
def test_assert_nq(self):
exp = {'msg': '登录成功'}
response = {'msg': '登录成功'}
assert exp != response, '断言失败,它们相等!'
# 真假断言--布尔值, True/False
def test_assert_bool(self):
assert random.choice([True, False]), '真假断言失败'
# 成员关系断言--in 包含关系断言
def test_assert_contains(self):
contains = 'pytest'
contains_lst = ['pytest', 'unittest', 'python']
assert contains in contains_lst
# 集合断言--不常用--在python中,集合是不考虑顺序的,只要元素不重复就行了
def test_assert_set(self):
set_a = {1, 2, 3}
set_b = {3, 2, 1}
assert set_a == set_b
if __name__ == '__main__':
pytest.main(['-vs', '-k', 'test_assert_result'])
六、pytest插件
使用插件务必先去安装,因为pytest默认是没有这个插件,可以去安装外部的插件,去使用 pytest 来执行插件,这就是为什么选择使用 pytest 框架的原因,因为 pytest 里面有丰富的插件系统,而 unittest 是没有这些插件的,所以 pytest 要比 unittest 测试框架更加灵活;
可参考学习:使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 18--测试框架Pytest基础 2--插件和参数化
第一个插件--并发执行(多线程/多进程)
pytest 框架提供一个可以并发测试的一个插件, pytest_xdist 提供了一个 -n 选项设置多进程/多线程数量,使测试用例可以在多个进程环境下并发执行,提高测试效率,在测试用例很多的情况下,可以设置并发去执行;
安装插件,安装成功后可以在 Python 解释器中查看;
pip install pytest-xdist

3、修改test_login.py文件内容;
# 导包--引入pytest模块框架
import pytest
from time import sleep
# 创建一个类 TestLogin--测试类以Test开头,后面可自定义
class TestLogin:
# 定义一个测试函数(测试方法)test_login_success
def test_login_success(self):
sleep(3)
print('登录成功场景')
# 登录失败
def test_login_failed(self):
sleep(3)
print('登录失败场景')
# 登录错误
def test_login_error(self):
sleep(3)
print('登录错误场景')
4、添加强制等待时间测试查看效果,运行test_login.py文件或者使用 pytest -k test_login 命令去执行,能够看到"每次执行一个测试用例则需要等待3秒,3个测试用例时间--3 passed in 9.06s";

而使用 pytest -k test_login -n 3 命令去执行,则是只需一次等待3秒,"3个测试用例时间,3 passed in 3.76s)";当测试文件、测试用例很多的情况下,使用 多进程/多线程 这种 并发执行 方式,可以节省测试时间;

亦可在主函数里面进行设置运行;
# 导包--引入pytest模块框架
import pytest
from time import sleep
# 创建一个类 TestLogin--测试类以Test开头,后面可自定义
class TestLogin:
# 定义一个测试函数(测试方法)test_login_success
def test_login_success(self):
sleep(3)
print('登录成功场景')
# 登录失败
def test_login_failed(self):
sleep(3)
print('登录失败场景')
# 登录错误
def test_login_error(self):
sleep(3)
print('登录错误场景')
# 主函数
if __name__ == '__main__':
# 使用参数--怎么传参数?--使用list类型
pytest.main(['-s', '-v', '-n3'])
5、修改pytest.ini配置文件内容后,再直接运行run.py文件,查看测试执行效果;命令行、主函数、配置文件中可直接 "-n3",这里为了格式好看所以加了空格(主函数中不能加空格,否则不生效,因为一般不用加,大部分都是写在配置文件里面,建议按需自行选择)。
[pytest]
addopts = -s -v -n 3
testpaths = ./testcases
python_files = test_*.py
python_classes = Test*
python_functions = test_*
第二个插件--失败测试用例重跑
这个插件在unittest中是没有的,它是pytest中的一个插件;当做自动化测试时,执行测试用例,遇到网络波动或其他不确定因素导致测试用例运行失败,这并不是测试失败(因为测试环境等因素导致的偶发性的失败),这时需要用到失败重试运行。
安装插件,若没有安装就去使用的话是会报错的;
pip install pytest-rerunfailures

6、修改test_login.py文件内容,设置断言效果,当 assert 断言为 True 时,3个测试用例执行成功,当 assert 断言为 False 时,有断言的测试用例则执行失败;
# 导包--引入pytest模块框架
import pytest
import random
# 创建一个类 TestLogin--测试类以Test开头,后面可自定义
class TestLogin:
# 定义一个测试函数(测试方法)test_login_success
def test_login_success(self):
# assert断言--random随机函数--随机取列表中的值True, False
assert random.choice([True, False]), '测试失败重跑'
print('登录成功场景')
# 登录失败
def test_login_failed(self):
print('登录失败场景')
# 登录错误
def test_login_error(self):
print('登录错误场景')
# 主函数
if __name__ == '__main__':
pytest.main(['-s', '-v'])
7、修改pytest.ini配置文件,去掉 -n 参数;
[pytest]
addopts = -s -v
testpaths = ./testcases
python_files = test_*.py
python_classes = Test*
python_functions = test_*
8、通过添加装饰器的方式可以让测试用例执行出现失败时重新再去执行,修改test_login.py文件内容,添加装饰器,先对单个测试用例进行失败重跑设置;关注平时跑的测试用例,哪一个用例波动比较大的,可以在那一个测试用例的前面添加 @pytest.mark.flaky() 装饰器,去设置重跑次数,里面需要用到两个参数,第一个 reruns=3 参数是当用例失败时会重跑次数(指定失败时重跑的次数),第二个 reruns_delay=2 参数是指定重试之间的延迟时间;这是对单个测试用例去设置重跑的次数;,根据平时跑的一个结果,去判定哪条测试用例报错比较多,就在那条测试用例加上失败重跑调试;
# 导包--引入pytest模块框架
import pytest
import random
# 创建一个类 TestLogin--测试类以Test开头,后面可自定义
class TestLogin:
# 装饰器--reruns=3 失败重跑的次数设置,reruns_delay=2 失败重跑的间隔时间设置--这是对单个测试用例进行失败重跑设置
@pytest.mark.flaky(reruns=3, reruns_delay=2)
# 定义一个测试函数(测试方法)test_login_success
def test_login_success(self):
# assert断言--random随机函数--随机取列表中的值True, False
assert random.choice([True, False]), '测试失败重跑'
print('登录成功场景')
# 登录失败
def test_login_failed(self):
print('登录失败场景')
# 登录错误
def test_login_error(self):
print('登录错误场景')
# 主函数
if __name__ == '__main__':
pytest.main(['-s', '-v'])
9、对所有测试用例进行失败重跑设置,可以写到配置文件 pytest.ini 里面去;修改pytest.ini配置文件,设置全局需要两个横杠"--reruns 3" ,为所有的测试用例默认加上失败重跑的次数
[pytest]
addopts = -s -v --reruns 3
testpaths = ./testcase
python_files = test_*.py
python_classes = Test*
python_functions = test*
10、修改test_login.py文件内容,设置全局;再运行run.py文件,执行主函数查看测试效果;
# 导包--引入pytest模块框架
import pytest
import random
# 创建一个类 TestLogin--测试类以Test开头,后面可自定义
class TestLogin:
# 定义一个测试函数(测试方法)test_login_success
def test_login_success(self):
# assert断言--random随机函数--随机取列表中的值True, False
assert random.choice([True, False]), '测试失败重跑'
print('登录成功场景')
# 登录失败
def test_login_failed(self):
assert random.choice([True, False]), '测试失败重跑'
print('登录失败场景')
# 登录错误
def test_login_error(self):
assert random.choice([True, False]), '测试失败重跑'
print('登录错误场景')
# 主函数
if __name__ == '__main__':
pytest.main()
总结,对某一个测试用例使用装饰器是单个的,在配置文件中是全局设置;需根据实际情况,知道某个接口、某个功能它出现问题的概率比较大的话,可以在单个的测试用例里面去加上装饰器的标识,出现失败时它会试着重跑测试用例。
第三个插件--测试用例执行顺序
因为在默认情况下,pytest 会以特定的顺序运行测试用例;可以使用 pytest-ordering 插件去自定义设置测试用例的执行顺序;
通常是按照测试文件中定义的顺序去执行,但是有的时候,测试用例的执行顺序对于特定的场景可能很重要,此时就需要更加精细的去控制测试用例的执行顺序;比如要去删除某一个用户,这种情况首先得先去跑执行新增创建用户的用例,只有新增完成之后,才能有数据去调用删除接口;
安装插件,通过插件去改变测试用例的执行顺序;
pip install pytest-ordering

第一种方式,使用装饰器 @pytest.mark.run(order=1) 通过 order 等于多少值的方式去调用插件里面的 run 方法来设置执行顺序(当设置的编号相同时,就根据文件设置的编码去执行了);
11、修改test_adduser.py文件内容,测试查看;
# 导包
import pytest
# 创建类 TestAddUser
class TestAddUser():
# 装饰器 @pytest.mark.run(order=1) 指定执行顺序
@pytest.mark.run(order=3)
# 定义测试用例 test_add_user_01
def test_add_user_01(self):
print('新增用户01')
@pytest.mark.run(order=2)
def test_add_user_02(self):
print('新增用户02')
@pytest.mark.run(order=1)
def test_add_user_03(self):
print('新增用户03')
if __name__ == '__main__':
pytest.main(['-vs', '-k', 'test_adduser'])
第二种方式,使用装饰器 标记的方式,需要先在 pytest.ini 配置文件中注册自定义的标记;
12、修改test_adduser.py文件内容,先不添加注册标记,此时执行test_adduser.py文件,可以执行成功,但是会出现警告信息,因为还没有在配置文件中注册;运行后若发现警告信息(大致意思是"pytest 识别到了未注册的自定义标记",如果使用的自定义标记不是 pytest 中自带的,而是个人自定义的时候就会出现警告信息),可以通过在 pytest.ini 配置文件中使用 markers 注册自定义标记来消除警告信息;
# 导包
import pytest
# 创建类 TestAddUser
class TestAddUser():
# 装饰器 标记方式
@pytest.mark.last
# 定义测试用例 test_add_user_01
def test_add_user_01(self):
print('新增用户01')
@pytest.mark.second
def test_add_user_02(self):
print('新增用户02')
@pytest.mark.first
def test_add_user_03(self):
print('新增用户03')
if __name__ == '__main__':
pytest.main(['-vs', '-k', 'test_adduser'])

13、修改pytest.ini配置文件内容,添加自定义注册标记定义,这时再执行test_adduser.py文件,运行成功,没有警告信息。
[pytest]
addopts = -s -v --reruns 3
testpaths = ./testcase
python_files = test_*.py
python_classes = Test*
python_functions = test*
# 注册自定义标记
markers =
last
second
first

未完待续。。。