pytest 第三章 fixture及作用范围
摘要:setup和teardown是作用于全局的,如果一个测试类中,不同的case有不同的前置后置操作,就没法用setup和teardown解决问题。所以我们需要自定义测试的预置条件。
文章目录
fixture的优势
fixture的优势有以下几点:
- 命名方式灵活且明确,不局限于setup/teardown这几个命名
- fixture以模块化的方式实现,因为每个fixture名称都会触发调用fixture函数,该函数本身可以使用其他fixtures
- conftest.py文件里可以实现数据共享,不需要import就能自动找到一些配置
- 允许根据配置和组件选项对fixtures和测试用例进行参数化,或者在测试方法/类/模块或者整个测试会话范围内重复使用该fixture
调用fixture的三种方法
调用fixture的三种方法:
- 函数或类方法直接传fixture函数名作为参数
- 使用装饰器@pytest.mark.usefixtures()
- autouse=True自动使用
fixture函数名作为参数使用
函数通过@pytest.fixture装饰器注册成为一个fixture函数,函数返回的对象可作为参数传入测试方法中
import pytest
@pytest.fixture()
def get_a():
print('\n执行fixture函数')
def test001(get_a):
print('\n执行test001')
assert True
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 1 item
test_b.py
执行fixture函数
执行test001
.
============================== 1 passed in 0.01s ===============================
如上面的例子,fixture函数get_a没有返回值,被作为参数传入test001中之后被优先执行。fixture函数有无返回值都可以。Fixtures允许测试方法能轻松引入预先定义好的初始化准备函数,而无需关心导入/设置/清理方法的细节。 这是依赖注入的一个主要示例,其中fixture函数的功能扮演”注入器“的角色,测试方法来“消费”这些fixture对象。
autouse=True自动使用
#test_b.py
import pytest
def test001():
print('\n执行test001')
def test002():
print('\n执行test002')
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
#conftest.py
import pytest
@pytest.fixture(scope="function",autouse=True)
def get_a():
a=1
print('\n执行fixture函数>>>>>>>>get_a(function)')
return a
@pytest.fixture(scope="module",autouse=True)
def get_b():
b=2
print('\n执行fixture函数>>>>>>>>get_b(module)')
return b
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items
test_b.py
执行fixture函数>>>>>>>>get_b(module)
执行fixture函数>>>>>>>>get_a(function)
执行test001
.
执行fixture函数>>>>>>>>get_a(function)
执行test002
.
============================== 2 passed in 0.01s ===============================
如上示例所示,fixture函数使用autouse=True之后,测试case无需传fixture函数名,就能被自动使用。
弊端:这样目前无法应对测试用例需调用fixture函数返回值的情况。
使用装饰器@pytest.mark.usefixtures()
可以使用装饰器**@pytest.mark.usefixtures()**
#test_b.py
import pytest
@pytest.mark.usefixtures('get_b')
@pytest.mark.usefixtures('get_a')
def test001():
print('\n执行test001')
@pytest.mark.usefixtures('get_a')
def test002():
print('\n执行test002')
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
#conftest.py
import pytest
@pytest.fixture(scope="function")
def get_a(request):
a=1
print('\n执行fixture函数>>>>>>>>get_a(function)')
return a
@pytest.fixture(scope="module")
def get_b(request):
b=2
print('\n执行fixture函数>>>>>>>>get_b(module)')
return b
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items
test_b.py
执行fixture函数>>>>>>>>get_b(module)
执行fixture函数>>>>>>>>get_a(function)
执行test001
.
执行fixture函数>>>>>>>>get_a(function)
执行test002
.
============================== 2 passed in 0.01s ===============================
fixture生效范围
fixture用scope参数标示4个生效范围:
- function(默认):每个函数或方法都会调用
- class:每个类调用一次
- module:每个py文件调用一次
- session:跨py文件调用,一次执行中调用一次
作用范围是session>module>class>function
下面看示例:
function
import pytest
@pytest.fixture(scope="class")
def get_a():
a=1
print('\n执行fixture函数')
return a
def test001(get_a):
print('\n执行test001')
print("获取a:%d" % get_a)
assert True
def test002(get_a):
print('\n执行test002')
print("获取a:%d" % get_a)
assert True
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items
test_b.py
执行fixture函数
执行test001
获取a:1
.
执行fixture函数
执行test002
获取a:1
.
============================== 2 passed in 0.01s ===============================
function:如果函数将fixture函数作为参数传入,fixture函数在函数之前执行一次。未传入不执行,方法中传入也不执行,仅作用于函数,且为同py文件的函数。
class
import pytest
@pytest.fixture(scope="class")
def get_a():
a=1
print('\n执行fixture函数')
return a
class TestCase:
def test001(self,get_a):
print('\n执行test001')
print("获取a:%d" % get_a)
assert True
def test002(self,get_a):
print('\n执行test002')
print("获取a:%d" % get_a)
assert True
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items
test_b.py
执行fixture函数
执行test001
获取a:1
.
执行test002
获取a:1
.
============================== 2 passed in 0.02s ===============================
class:类中所有方法都传入了fixture函数,则fixture函数在类开始时执行一次。
import pytest
@pytest.fixture(scope="class")
def get_a():
a=1
print('\n执行fixture函数')
return a
class TestCase:
def test001(self):
print('\n执行test001')
assert True
def test002(self,get_a):
print('\n执行test002')
print("获取a:%d" % get_a)
assert True
def test003(self):
print('\n执行test003')
assert True
def test004(self,get_a):
print('\n执行test004')
print("获取a:%d" % get_a)
assert True
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 4 items
test_b.py
执行test001
.
执行fixture函数
执行test002
获取a:1
.
执行test003
.
执行test004
获取a:1
.
============================== 4 passed in 0.02s ===============================
class:如果fixture函数在类中并非全部方法都传入,则在第一个传入fixture函数的方法执行前执行一次,其余方法前不执行。(也就是说不管传入fixture函数的方法有一个还是多个,fixture函数只会在一个类中执行一次,在第一个传入fixture函数的方法执行前执行)。
在函数中传入范围为class的fixture函数,fixture函数作用等同于作用范围function,见如下示例
import pytest
@pytest.fixture(scope="class")
def get_a():
a=1
print('\n执行fixture函数')
return a
def test000(get_a):
print('\n执行test000')
def test0000(get_a):
print('\n执行test0000')
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items
test_b.py
执行fixture函数
执行test000
.
执行fixture函数
执行test0000
.
============================== 2 passed in 0.01s ===============================
module
import pytest
@pytest.fixture(scope="module")
def get_a():
a=1
print('\n执行fixture函数')
return a
def test001(get_a):
print('\n执行test001')
print("获取a:%d" % get_a)
assert True
class TestCase:
def test005(self,get_a):
print('\n执行test005')
print("获取a:%d" % get_a)
assert True
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items
test_b.py
执行fixture函数
执行test001
获取a:1
.
执行test005
获取a:1
.
============================== 2 passed in 0.01s ===============================
module:可作用于一个py文件内的函数和方法,一个py文件内仅执行一次,在第一个传入fixture函数的函数或方法执行之前执行。
session
#test_b.py
def test001(get_a):
print('\n执行test001')
class TestCase:
def test002(self,get_a):
print('\n执行test001')
#test_c.py
def test003(get_a):
print('\n执行test003')
class TestCase1:
def test004(self, get_a):
print('\n执行test004')
#conftest.py
import pytest
@pytest.fixture(scope="session")
def get_a():
a=1
print('\n执行fixture函数')
return a
执行结果:
=========================================================== test session starts ============================================================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 4 items
test_b.py
执行fixture函数
执行test001
.
执行test001
.
test_c.py
执行fixture函数
执行test003
.
执行test004
.
============================================================ 4 passed in 0.03s ========================================================
session:一次pytest执行中只执行一次,在第一个传入fixture函数的方法或函数执行前执行,需要结合conftest.py文件使用,可跨py文件执行。
作用范围混合使用
#test_b.py
def test001(get_a):
print('\n执行test001')
class TestCase:
def test002(self,get_b):
print('\n执行test002')
#test_c.py
def test003(get_d):
print('\n执行test003')
class TestCase1:
def test004(self, get_c):
print('\n执行test004')
#conftest.py
import pytest
@pytest.fixture(scope="session")
def get_a():
a=1
print('\n执行fixture函数>>>>>>>>get_a(session)')
return a
@pytest.fixture(scope="module")
def get_b():
b=2
print('\n执行fixture函数>>>>>>>>get_b(module)')
return b
@pytest.fixture(scope="class")
def get_c():
c=3
print('\n执行fixture函数>>>>>>>>get_c(class)')
return c
@pytest.fixture(scope="function")
def get_d():
d=4
print('\n执行fixture函数>>>>>>>>get_d(function)')
return d
pytest执行结果:
=========================================================== test session starts ============================================================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 4 items
test_b.py
执行fixture函数>>>>>>>>get_a(session)
执行test001
.
执行fixture函数>>>>>>>>get_b(module)
执行test002
.
test_c.py
执行fixture函数>>>>>>>>get_d(function)
执行test003
.
执行fixture函数>>>>>>>>get_c(class)
执行test004
.
============================================================ 4 passed in 0.02s =============================================================
如示例中所示,get_a>test001>get_b>test002>get_d>test003>get_c>test004
只有class级别的fixture函数,会对类外函数产生function级别的效果,见如下示例
#test_b.py
import pytest
def test001(get_a):
print('\n执行test001')
def test002(get_a):
print('\n执行test002')
class TestCase:
def test003(self,get_a):
print('\n执行test003')
def test004(self,get_a):
print('\n执行test003')
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
#conftest.py
import pytest
@pytest.fixture(scope="class")
def get_a():
a=1
print('\n执行fixture函数>>>>>>>>get_a(class)')
return a
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 4 items
test_b.py
执行fixture函数>>>>>>>>get_a(class)
执行test001
.
执行fixture函数>>>>>>>>get_a(class)
执行test002
.
执行fixture函数>>>>>>>>get_a(class)
执行test003
.
执行test003
.
============================== 4 passed in 0.01s ===============================
如上述示例所示,class级别的fixture函数get_a在类外对每一个方法都在前面执行了一遍,session和module级别的fixture函数则没有该效果。
fixture函数之间的互相调用
fixture函数之间可以实现互相调用。
#test_b.py
import pytest
def test001(get_a):
print(get_a)
print('\n执行test001')
def test002(get_a):
print(get_a)
print('\n执行test002')
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
#conftest.py
import pytest
@pytest.fixture(scope="class")
def get_a(get_b):
a=get_b+2
print('\n执行fixture函数>>>>>>>>get_a(class)')
return a
@pytest.fixture(scope="class")
def get_b():
b=2
print('\n执行fixture函数>>>>>>>>get_b(class)')
return b
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items
test_b.py
执行fixture函数>>>>>>>>get_b(class)
执行fixture函数>>>>>>>>get_a(class)
4
执行test001
.
执行fixture函数>>>>>>>>get_b(class)
执行fixture函数>>>>>>>>get_a(class)
4
执行test002
.
============================== 2 passed in 0.01s ===============================
如同示例中的fixture函数get_a和get_b,get_b作为参数传入get_a。
需要注意的是,fixture函数之间互相调用必须遵循以下规则:
- 作为参数的fixtrue函数作用范围必须大于等于主fixture函数。比如在上述示例中,get_b的作用范围必须大于get_a,否则会发生fixture函数无法访问的error
- 作为参数的fixture函数作用范围大于主fixture函数,主fixture函数被case调用,则作为参数的fixture函数依然按自己的作用范围生效。见如下示例。
#test_b.py
import pytest
def test001(get_a):
print(get_a)
print('\n执行test001')
def test002(get_a):
print(get_a)
print('\n执行test002')
if __name__ == '__main__':
pytest.main(['-s','test_b.py'])
#conftest.py
import pytest
@pytest.fixture(scope="function")
def get_a(get_b):
a=get_b+2
print('\n执行fixture函数>>>>>>>>get_a(function)')
return a
@pytest.fixture(scope="module")
def get_b():
b=2
print('\n执行fixture函数>>>>>>>>get_b(module)')
return b
pytest执行结果:
============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items
test_b.py
执行fixture函数>>>>>>>>get_b(module)
执行fixture函数>>>>>>>>get_a(function)
4
执行test001
.
执行fixture函数>>>>>>>>get_a(function)
4
执行test002
.
============================== 2 passed in 0.01s ===============================
文章评论