pytest的fixture中文介绍可参考(不过文档稍微有点老):
https://www.osgeo.cn/pytest/fixture.html#what-fixtures-are
pytest各个作用域的fixture
- scope = “function”
可作用于每个用例
fixture使用的声明放在类定义前面,类中的每个用例执行时都会调用fixture装饰函数
fixture使用的声明放在用例前,用例执行时会调用fixture装饰函数 - scope = “class”
作用于整个类
fixture使用的声明放在类定义前面,类中的第一个用例执行时会调用fixture装饰函数一次
fixture使用的声明放在类中的用例前,用例执行时会调用fixture装饰函数一次,之后的用例即时有@pytest.mark.usefixtures(“fixture_class”),也不会执行 - scope = “module”
作用于整个python文件
在整个python文件中只会调用一次,
不管@pytest.mark.usefixtures(“fixture_module”)声明放在哪里(类外的用例前、类的声明前或这个类中的用例前),fixture函数都只会在整个python文件执行第一个用例时调用一次 - scope = “session”
作用于整个会话,通常可以放在conftest.py文件中,作为全局使用的前、后置步骤
在所有地方都可以使用@pytest.mark.usefixtures(“fixture_session”)
例:
import pytest
@pytest.fixture(scope="function")
def fixture_function():
print("fixture_function ####")
@pytest.fixture(scope="class")
def fixture_class():
print("fixture_class ----")
@pytest.fixture(scope="module")
def fixture_module():
print("fixture_module @@@@")
def test_0():
print('test_0')
@pytest.mark.usefixtures("fixture_class")
@pytest.mark.usefixtures("fixture_function")
class Test_class():
@pytest.mark.usefixtures("fixture_module")
def test_1(self):
print('test_1')
def test_2(self):
print('test_2')
def test_3(self):
print('test_3')
def test_4(self):
print('test_4')
打印内容如下:
============================== 4 passed in 0.23s ==============================
fixture_module @@@@
fixture_class ----
fixture_function ####
PASSED [ 25%]test_1
fixture_function ####
PASSED [ 50%]test_2
fixture_function ####
PASSED [ 75%]test_3
fixture_function ####
PASSED [100%]test_4
Process finished with exit code 0
1.2.pytest添加fixture装饰实现前置、后置方法
可以通过fixture夹具实现前置后置方法,后置需要使用yeild来实现。
如果一个方法或者一个类想要同时调用多个fixture。有两种方法:
- 可以使用@pytest.mark.usefixtures()进行叠加。
注意叠加顺序,先执行的后添加@pytest.mark.usefixtures语句,后执行的先添加。
需注意:与直接传入fixture不同的是,@pytest.mark.usefixtures无法获取到被fixture装饰的函数的返回值;
@pytest.mark.usefixtures的使用场景是:被测试函数需要多个fixture做前后置工作时使用; - 可以在方法中添加多个fixture函数名作为入参,执行顺序:先入参的后调用。
具体如下:
import pytest
class Test_hhh:
@pytest.fixture
def setup_step1(self):
print("setup_step1 @@@@")
@pytest.fixture
def setup_and_teardown_step2(self):
print("setup_step2 ####")
yield
print('teardown_setp2 ####\n')
@pytest.mark.usefixtures("setup_step1")
def test_1(self):
print('test_1')
@pytest.mark.usefixtures("setup_step1")
@pytest.mark.usefixtures("setup_and_teardown_step2")
def test_2(self):
print('test_2')
def test_3(self, setup_step1, setup_and_teardown_step2):
print('test_3')
def test_4(self, setup_and_teardown_step2):
print('test_4')
setup_step1()、setup_and_teardown_step2()两个函数前添加了 @pytest.fixture 装饰,scope默认是"function"。
每次执行test_1用例之前,都会先调用setup_step1()。
每次执行test_2用例之前,都会先调用setup_and_teardown_step2(),再调用setup_step1()。
每次调用test_3用例之前,都会先调用setup_and_teardown_step2(),再调用setup_step1()。
每次调用test_4用例之前,都会先调用setup_and_teardown_step2()。
打印顺序如下:
============================== 4 passed in 0.21s ==============================
setup_step1 @@@@
PASSED [ 25%]test_1
setup_step2 ####
setup_step1 @@@@
PASSED [ 50%]test_2
teardown_setp2 ####
setup_step1 @@@@
setup_step2 ####
PASSED [ 75%]test_3
teardown_setp2 ####
setup_step2 ####
PASSED [100%]test_4
teardown_setp2 ####
Process finished with exit code 0
pytest中各个级别的setup和teardown方法
除了使用fixture来实现前置、后置。pytest也可以直接使用setup和teardown方案实现前置、后置。pytest的前置、后置分为方法级、类级和模块级。
- 方法级:setup_method() teardown_method()
- 类级别:setup_class() teardown_class()
- 模块级:setup_module() teardown_method()
def setup_module():
print("setup_module")
def teardown_module():
print("teardown_module")
def test_1():
print("test_1")
assert 1 == 1
class Test_learn:
def setup_method(self):
print("setup_method")
def teardown_method(self):
print("teardown_method")
def setup_class(self):
print("setup_class")
def teardown_class(self):
print("teardown_class")
def test_2(self):
print("test_2")
assert True
def test_3(self):
print("test_3")
在整个文件中的第一个用例执行前,setup_module()会被调用。
在整个文件中的最后一个用例执行后,teardown_module()会被调用。
在类中的第一个用例执行前,setup_calss()会被调用。
在类中的最后一个用例执行后,teardown_class()会被调用。
在类中的每个用例执行前,setup_method()会被调用。
在类中的每个用例执行后,teardown_method()会被调用。
打印结果如下:
============================= test session starts =============================
collecting ... collected 3 items
test_file.py::test_1
setup_module
test_1
PASSED
test_file.py::Test_learn::test_2
setup_class
setup_method
test_2
PASSEDteardown_method
test_file.py::Test_learn::test_3
setup_method
test_3
PASSEDteardown_method
teardown_class
teardown_module
============================== 3 passed in 0.04s ==============================
pytest参数化
可以使用@pytest.mark.parametrize进行参数化,具体用法如下:
import pytest
class Test_learn:
test_data = [
['正常登录', 'user1', 'password1', '登录成功'],
['密码错误', 'user2', 'password2', '用户名或密码错误,请重新输入']
]
ids = [i[0] for i in test_data]
@pytest.mark.parametrize("case, username, password, expect_text", test_data, ids=ids)
def test_1(self, case, username, password, expect_text):
print('\n',case,username, password, expect_text)
assert True
data2 = [
{"username": "张三", "password":"123456"},
{"username": "李四", "password": "123456"}
]
@pytest.mark.parametrize("dic",data2)
def test_2(self, dic):
print('\n',dic['username'], dic['password'])
assert True
@pytest.mark.parametrize()中,第一个参数参数名(可以是一个或多个),第二个参数test_data为具体数据,第三个参数ids为用例名,参数名和数组中元素按顺序对应取值。
如上第一个用例中,数据有两条。第一条参数:case=‘登录成功’,username=‘user1’, password=‘password1’, expect_text=‘登录成功’;第一条用例的名称:'登录成功’。
参数的数据也可以是字典、元组等。
执行打印如下:
============================= test session starts =============================
collecting ... collected 4 items
test_file.py::Test_learn::test_1[\u6b63\u5e38\u767b\u5f55]
正常登录 user1 password1 登录成功
PASSED
test_file.py::Test_learn::test_1[\u5bc6\u7801\u9519\u8bef]
密码错误 user2 password2 用户名或密码错误,请重新输入
PASSED
test_file.py::Test_learn::test_2[dic0]
张三 123456
PASSED
test_file.py::Test_learn::test_2[dic1]
李四 123456
PASSED
============================== 4 passed in 0.04s ==============================
pytest跳过用例
- 无条件跳过用例
@pytest.mark.skip - 条件成立时,跳过用例
@pytest.mark.skipif(表达式, reason=‘跳过用例的原因’)
如:
import pytest
# 会被跳过的
@pytest.mark.skipif(4%2 == 0, reason='跳过用例的原因')
def test_skip_if_true():
var = 'world'
assert var == 'world'
# 不会被跳过的
@pytest.mark.skipif(5%2 == 0, reason='跳过用例的原因')
def test_skip_if_false():
var = 2
assert var == 2
@pytest.mark.skip
def test_skip():
var = 'HELLO'
assert var == 'HELLO'
打印结果:
============================= test session starts =============================
collecting ... collected 3 items
test_file.py::test_skip_if_true SKIPPED (跳过用例的原因)
Skipped: 跳过用例的原因
test_file.py::test_skip_if_false PASSED
test_file.py::test_skip SKIPPED (unconditional skip)
Skipped: unconditional skip
======================== 1 passed, 2 skipped in 0.03s =========================
pytest指定用例的执行顺序
需要安装插件:
pip install pytest-ordering
通过如下装饰器来指定执行顺序,order为整型,值越小,越先执行。
@pytest.mark.run(order=n)
n>=0的情况,有装饰器的用例比没有顺序装饰器的用例先执行;
n<0的情况,有装饰器的用例比没有顺序装饰器的用例后执行
例子:
import pytest
@pytest.mark.run(order=1)
def test_1():
var = 'world'
assert var == 'world'
@pytest.mark.run(order=0)
def test_2():
var = 2
assert var == 2
@pytest.mark.run(order=-1)
def test_3():
var = 'HELLO'
assert var == 'HELLO'
def test_4():
assert True
执行结果:
============================= test session starts =============================
collecting ... collected 4 items
test_file.py::test_2 PASSED
test_file.py::test_1 PASSED
test_file.py::test_4 PASSED
test_file.py::test_3 PASSED
============================== 4 passed in 0.02s ==============================
pytest用例执行重试
需要安装第三方插件:
pip install pytest-rerunfailures
使用方式有两种:
-
直接在命令行指定 reruns表示最大重试次数,reruns-delay表示每次重试前的等待时间
pytest -s -q --reruns=2 --reruns-delay=5 {cases_path} --alluredir {report_path}
-
使用@pytest.mark.flaky装饰器,reruns表示最大重试次数,reruns_delay表示每次重试前的等待时间,如: @pytest.mark.flaky(reruns=3, reruns_delay=2)
例:
import pytest
@pytest.mark.run(order=-1)
def test_1():
var = 'world'
assert var == 'world'
@pytest.mark.flaky(reruns=3, reruns_delay=2)
def test_4():
assert False
执行结果如下,test_4执行了四次,因为执行失败进行重试而执行的有三次。
============================= test session starts =============================
collecting ... collected 2 items
test_file.py::test_4 RERUN
test_file.py::test_4 RERUN
test_file.py::test_4 RERUN
test_file.py::test_4 FAILED
test_file.py:7 (test_4)
@pytest.mark.flaky(reruns=3, reruns_delay=2)
def test_4():
> assert False
E assert False
test_file.py:10: AssertionError
test_file.py::test_1 PASSED
==================== 1 failed, 1 passed, 3 rerun in 6.15s =====================
进程已结束,退出代码为 1