fixtures 之间互调:
pytest 中 fixtures 之间也可以互相调用:
# conftest.py
import pytest
@pytest.fixture()
def fix_init_1():
print(" \nfix_init_1...")
@pytest.fixture()
def fix_init_2(fix_init_1):
print(" \nfix_init_2...")
@pytest.fixture()
def fix_init_3(fix_init_2):
print(" \nfix_init_3...")
# test_moduleName.py
import pytest
class TestClassName:
"""测试类"""
def test_func(self, fix_init_3):
"""测试方法"""
pass
# 执行结果
============================= test session starts =============================
test_moduleName.py::TestClassName::test_func
fix_init_1...
fix_init_2...
fix_init_3...PASSED
============================== 1 passed in 0.02s ==============================
fixture 的作用域:
@pytest.fixture() 可以用 scope 参数来指定其作用域:
例如:@pytest.fixture(scope='class')
注意:当 fixture 作用域为 function 时,且装饰在测试类上,那么这个测试类的每个方法都会调用执行一次这个 fixture;
- function :fixture 默认的作用域,每个测试方法执行一次(如果被调用),默认时可以不用指定;
- class :每个测试类执行一次,如果同一个测试类中多次调用该 fixture 也只会执行一次;
- module :每个测试模块执行一次,如果同一个测试模块多次调用该 fixture 也只会执行一次;
- package :(可能淘汰,不建议使用)每个 package 中只执行一次,如果同一个 package 下多次调用,也只会执行一次;
- session :每次测试会话中只执行一次,在同一次会话中多次调用,也只会执行一次;
说明:当 fixture 有返回值时,pytest 会把返回值缓存起来,如果 fixture 在指定的作用域内被多次调用,只有第一次调用会真正的被执行,后续调用会使用被缓存起来的返回值,而不是再执行一遍;
# conftest.py
import pytest
# 作用域 function
@pytest.fixture(scope='function')
def fix_func():
print('\n方法级:fix_func...')
# 作用域 class
@pytest.fixture(scope='class')
def fix_class():
print('\n类级:fix_class...')
# 作用域 module
@pytest.fixture(scope='module')
def fix_module():
print('\n模块级:fix_module...')
# 作用域 session
@pytest.fixture(scope='session')
def fix_session():
print('\n会话级:fix_session...')
我们在3个测试方法中同时调用了4个级别的 fixture:
# test_moduleName.py
import pytest
class TestClass_1:
"""测试类1"""
@pytest.mark.usefixtures('fix_func')
@pytest.mark.usefixtures('fix_class')
@pytest.mark.usefixtures('fix_module')
@pytest.mark.usefixtures('fix_session')
def test_func_1(self):
"""测试方法1"""
pass
@pytest.mark.usefixtures('fix_func')
@pytest.mark.usefixtures('fix_class')
@pytest.mark.usefixtures('fix_module')
@pytest.mark.usefixtures('fix_session')
def test_func_2(self):
"""测试方法2"""
pass
class TestClass_2:
"""测试类2"""
@pytest.mark.usefixtures('fix_func')
@pytest.mark.usefixtures('fix_class')
@pytest.mark.usefixtures('fix_module')
@pytest.mark.usefixtures('fix_session')
def test_func_3(self):
"""测试方法3"""
pass
if __name__ == '__main__':
pytest.main(['-s'])
可以看到:
test_func_1 的调用全部被执行了;
test_func_2 的调用只有 function 级的被执行,因为 test_func_2 和 test_func_1 同属于同一个 session、module、class,所以这三个都不执行,只有 function 执行;
test_func_3 的调用执行了类级和方法级,因为 test_func_3 属于 另外一个类,所以 class 级会被再次调用;
# 执行结果:
============================= test session starts =============================
test_moduleName.py::TestClass_1::test_func_1
会话级:fix_session...
模块级:fix_module...
类级:fix_class...
方法级:fix_func...
PASSED
test_moduleName.py::TestClass_1::test_func_2
方法级:fix_func...
PASSED
test_moduleName.py::TestClass_2::test_func_3
类级:fix_class...
方法级:fix_func...
PASSED
============================== 3 passed in 0.02s ==============================
Process finished with exit code 0
fixture 的执行顺序:
前面讲过,fixture 可以同时使用多个,且 fixture 间可以相互调用,那么当一个测试方法同时调用多个fixture时,会根据各fixture作用域、依赖关系、传参顺序来确定fixture的执行顺序;
而且,执行顺序要分两种情况:
一. 用例前置(yield 之前):
- 先按作用域,先执行作用域高的(先session级,最后执行function级);
- 再按依赖关系,在执行某个fixture时,如果它调用了其他的fixture那就先执行被调用的fixture;
- 再按传参顺序,在测试方法中如果f1的传参位置在f2的前面,那么就先执行f1,再执行f2;
- 如果是用装饰器的方法使用fixtures,那么下方的fixture先执行,上方的fixture后执行;
- 如果同时用了传参和装饰器的方法使用fixtures,那么先执行装饰器的fixture,再执行传参的fixture;
二. 用例后置(yield 之后):
- 完全和前置的顺序相反,同一个 fixture 的前置越晚执行,后置就越早执行!
# conftest.py
import pytest
@pytest.fixture
def fix_func():
print('\n方法级-被调用方...')
yield
print('\n方法级-被调用方...')
@pytest.fixture
def fix_func_param_1(fix_func):
print('\n方法级-传参法-1...')
yield
print('\n方法级-传参法-1...')
@pytest.fixture
def fix_func_param_2():
print('\n方法级-传参法-2...')
yield
print('\n方法级-传参法-2...')
@pytest.fixture
def fix_func_param_3():
print('\n方法级-传参法-3...')
yield
print('\n方法级-传参法-3...')
@pytest.fixture
def fix_func_decorator_1(fix_func):
print('\n方法级-装饰器-1...')
yield
print('\n方法级-装饰器-1...')
@pytest.fixture
def fix_func_decorator_2():
print('\n方法级-装饰器-2...')
yield
print('\n方法级-装饰器-2...')
@pytest.fixture
def fix_func_decorator_3():
print('\n方法级-装饰器-3...')
yield
print('\n方法级-装饰器-3...')
# 作用域 class
@pytest.fixture(scope='class')
def fix_class():
print('\n类级:fix_class...')
yield
print('\n类级:fix_class...')
# 作用域 module
@pytest.fixture(scope='module')
def fix_module():
print('\n模块级:fix_module...')
yield
print('\n模块级:fix_module...')
# 作用域 session
@pytest.fixture(scope='session')
def fix_session():
print('\n会话级:fix_session...')
yield
print('\n会话级:fix_session...')
# test_moduleName.py
import pytest
class TestClass_1:
"""测试类1"""
@pytest.mark.usefixtures('fix_class')
@pytest.mark.usefixtures('fix_session')
@pytest.mark.usefixtures('fix_func_decorator_3')
@pytest.mark.usefixtures('fix_func_decorator_2')
@pytest.mark.usefixtures('fix_func_decorator_1')
def test_func(self, fix_func_param_1, fix_func_param_2, fix_func_param_3, fix_module):
"""测试方法"""
print('==============>>>执行测试用例<<<==============')
if __name__ == '__main__':
pytest.main(['-s'])
# 执行结果:
============================= test session starts =============================
test_moduleName.py::TestClass_1::test_func
会话级:fix_session...
模块级:fix_module...
类级:fix_class...
方法级-被调用方...
方法级-装饰器-1...
方法级-装饰器-2...
方法级-装饰器-3...
方法级-传参法-1...
方法级-传参法-2...
方法级-传参法-3...
==============>>>执行测试用例<<<==============
PASSED
方法级-传参法-3...
方法级-传参法-2...
方法级-传参法-1...
方法级-装饰器-3...
方法级-装饰器-2...
方法级-装饰器-1...
方法级-被调用方...
类级:fix_class...
模块级:fix_module...
会话级:fix_session...
============================== 1 passed in 0.03s ==============================
Process finished with exit code 0
fixture的 autouse 参数:
autouse 参数的作用是 自动执行,也就是说,这个 fixture 不用主动调用,它会在相应的作用域中自动执行,并且在同一作用域中优先级最高;
# conftest.py
import pytest
# 增加一个 fix_auto 夹具
@pytest.fixture(autouse=True)
def fix_auto():
print('\n方法级-自动执行...')
yield
print('\n方法级-自动执行...')
# test_moduleName.py
import pytest
class TestClass_1:
"""测试类1"""
@pytest.mark.usefixtures('fix_class')
@pytest.mark.usefixtures('fix_session')
@pytest.mark.usefixtures('fix_func_decorator_1')
def test_func(self, fix_func_param_1, fix_module):
"""测试方法"""
print('==============>>>执行测试用例<<<==============')
============================= test session starts =============================
test_moduleName.py::TestClass_1::test_func
会话级:fix_session...
模块级:fix_module...
类级:fix_class...
方法级-自动执行...
方法级-被调用方...
方法级-装饰器-1...
方法级-传参法-1...
==============>>>执行测试用例<<<==============
PASSED
方法级-传参法-1...
方法级-装饰器-1...
方法级-被调用方...
方法级-自动执行...
类级:fix_class...
模块级:fix_module...
会话级:fix_session...
============================== 1 passed in 0.03s ==============================