文章目录
在使用pytest执行测试脚本时,文件名需要以test_
开头或者 _test
结尾,对于脚本中测试的类以Test_
开头,函数或方法以test_
开头。
先了解下源码里怎么写的注释说明:
def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
"""这是一个标记工厂函数的装饰器.
不管是否带有参数,这个装饰器是用来定义一个fixture函数.
fixture函数的函数名被引用后,能够在运行测试用例前被调用:
测试模块或类可以使用pytest.marker.usefixtures(fixturename)来标记.
测试用例函数可以直接使用被装饰的fixture名称,作为测试函数的输入参数,在这种情况下,
fixture函数将返回一个fixture实例来注入测试用例函数,即作为入参传递。
fixture可以使用‘return’或者‘yield’语句来返回他们的值.使用‘yield’时,
‘yield’语句后的代码块将作为teardown代码执行,不管测试结果如何,必须只生成一次,
也就是说yield语句后的代码只有在最后一次调用后执行.
:参数 scope: 共享scope范围的fixture, 有以下几种:
``"function"`` (默认), ``"class"``, ``"module"``,
``"session"``,``"package"``(实验中) .
:参数 params: 一个可选的参数列表,能够使得fixture函数的多次调用和所有测试用例函数使用它.
:参数 autouse: 如果为True,那么该fixture函数将被激活,所有测试对象都能使用;
如果为False,那么激活该fixture函数需要显式引用.
:参数 ids: 字符串ids的列表,每个id对应着参数,以便于他们是测试id的一部分,
如果没有提供id,那么将由参数自动生成.
:参数 name: fixture的名称,默认是被装饰的函数的名字.
如果一个fixture在同一个模块中被定义使用,那么该fixture的函数名将被请求fixture的函数参数隐藏.
解决此问题的方法是命名该装饰函数:
``fixture_<fixturename>````@pytest.fixture(name='<fixturename>')``.
"""
fixture的调用
fixture函数和普通函数没有太多的差异,最大的不同就是在需要使用到的函数上面加上装饰器@pytest.fixture(). 函数都是有返回值的,不写return就默认返回None,测试用例在调用fixture时,是将被装饰的函数名作为变量来传参,相当于执行被装饰的函数,接着该函数的返回值也通过函数名传递。
# test_asParameter.py
import pytest
@pytest.fixture()
def test_1():
varName = 'IPC'
print('this is varName:%s'%varName)
return varName
def test_2(test_1): # 被装饰的函数名test_1作为变量来传参
assert test_1 == "IPC" # 该函数test_1的返回值VarName也通过函数名test_1传递
由于tuple、list、dict的存在,函数可以返回其中任意一种形式,所以就这样调用fixture的返回值:
# test_asParameterListTupleDict.py
import pytest
@pytest.fixture()
def test_list():
varName1 = 'IPC1'
varName2 = 'IPC2'
return varName1, varName2
def test_list_(test_list):
assert isinstance(test_list, tuple) == True
assert test_list[0] == "IPC1"
assert test_list[1] == "IPC2"
以上展示了一个测试用例函数中调用一个fixture函数,一个测试用例函数还可以调用多个不同的fixture:
# test_asParamterMulti.py
import pytest
@pytest.fixture()
def test_func1():
varName = 'IPC1'
print("test_func1:",varName)
return varName
@pytest.fixture()
def test_func2():
varName = 'IPC2'
print("test_func2:",varName)
return varName
@pytest.fixture()
def test_func3():
varList = ['IPC3', 'IPC4']
print("test_func3:",varList)
return varList
def test_funcs(test_func1, test_func2, test_func3):
assert test_func1 == 'IPC1'
assert test_func2 == 'IPC2'
assert test_func3[0] == 'IPC3'
assert test_func3[1] == 'IPC4'
fixture的作用范围:
@pytest.fixture(scope=‘function’)
scope = “function”为默认值,作用范围是每个测试用例调用前执行一次,销毁是在测试用例调用后,也就是:调用一次销毁一次。
# test_scopefunction.py
import pytest
@pytest.fixture(scope='function')
def test_1():
varName = "IPC1"
print("\n execute test_1 ")
return varName
@pytest.fixture()
def test_2():
varName = "IPC2"
print(" execute test_2 ")
return varName
def test_function1(test_1):
print(" test_function1 ")
assert test_1 == "IPC1"
def test_function2(test1,test_2):
print(" test_function2 ")
assert test_2 == "IPC2"
运行结果:
execute test_1
. test_function1
execute test_1
execute test_2
. test_function2
可以发现"execute test1"打印了两遍,原因就是在scope='function’时,fixture函数调用一次销毁一次。
同理可得,在类中执行测试也是一样的:
import pytest
@pytest.fixture(scope='function')
def test_1():
varName = "IPC1"
print("\n execute test_1 ")
return varName
@pytest.fixture()
def test_2():
varName = "IPC2"
print(" execute test_2 ")
return varName
class Test_function():
def test_function1(self, test_1):
print(" test_function1 ")
assert test_1 == "IPC1"
def test_function2(self, test_1, test_2):
print(" test_function2 ")
assert test_2 == "IPC2"
@pytest.fixture(scope=‘class’)
scope = “class”,若在同一个class中的多个测试用例都调用了同一个scope = “class”的fixture函数,那么在这个class中的所有测试用例调用前,被装饰的fixture函数只执行一次。
import pytest
@pytest.fixture(scope='class')
def test_1():
varName = "IPC1"
print("\n execute test_1 once in class")
return varName
@pytest.fixture(scope='class')
def test_2():
varName = "IPC2"
print(" execute test_2 once in class")
return varName
class Test_class():
def test_class1(self, test_1, test_2):
print(" test_class1 ")
assert test_1 == "IPC1"
assert test_2 == "IPC2"
def test_class2(self, test_1, test_2):
print(" test_class2 ")
assert test_1 == "IPC1"
assert test_2 == "IPC2"
def test_3(test_1):
print(" execute test_3")
assert test_1 == "IPC1"
运行结果:
execute test_1 once in class
execute test_2 once in class
. test_class1
. test_class2
execute test_1 once in class
. execute test_3
可以发现,虽然在Test_class中分别调用了test_1、test_2各两次,但是"execute test_1 once in class"和"execute test_2 once in class"各只打印了一遍;“execute test_1 once in class"第二次打印是因为调用它的不是该类中的函数,而是普通的函数。所以当scope='class’时,对于该Test_class中的所有测试用例,在调用fixture前,fixture函数只执行一次。这样可以减少fixture函数的反复执行,提高运行效率。
@pytest.fixture(scope=‘module’)
scope=‘module’,在当前运行的脚本中(即当前module,在Python中一个.py脚本视为一个module),所有的测试用例在调用fixture前,被装饰的fixture函数只执行一次。
import pytest
@pytest.fixture(scope='module')
def test_1():
varName = "IPC1"
print("\n only execute test_1 one time ")
return varName
@pytest.fixture(scope='module')
def test_2():
varName = "IPC2"
print("only execute test_2 one time ")
return varName
def test_module1(test_1):
print("func test_module1 ")
assert test_1 == "IPC1"
def test_module2(test_1, test_2):
print("func test_module2 ")
assert test_2 == "IPC2"
class Test_module():
def test_module1(self, test_1, test_2):
print(" class test_module1 ")
assert test_1 == "IPC1"
assert test_2 == "IPC2"
def test_module2(self, test_1, test_2):
print(" class test_module2 ")
assert test_1 == "IPC1"
assert test_2 == "IPC2"
运行结果:
only execute test_1 one time
.func test_module1
only execute test_2 one time
.func test_module2
. class test_module1
. class test_module2
可以发现,虽然分别调用了test_1、test_2多次,但是"only execute test_1 one time"和"only execute test_2 one time"只打印了一遍,原因就是当scope='module’时,对于该Test_class中的所有测试用例(无论是类还是类外的函数),在调用fixture前,fixture函数只执行一次。
@pytest.fixture(scope=‘session’)
scope=‘session’,在一个工程项目中有多个py脚本,每个脚本中有一个或多个测试用例,当使用scope='session’来装饰fixture函数时,多个测试用例调用fixture函数前,该fixture函数只用执行一次。为了统一管理这些fixture函数,pytest中使用conftest.py文件来放置参数为scope='session’的fixture函数,conftest.py
的名字是固定的,pytest在运行时会自动识别。
# conftest.py
import pytest
@pytest.fixture(scope='session')
def test_session1():
varName = "IPC1"
print("\n test_session1 in conftest.py ")
return varName
@pytest.fixture(scope='session')
def test_session2():
varName = "IPC2"
print("test_session2 conftest.py ")
return varName
# test_session.py
def test_1(test_session1):
print("func test_1 in test_session.py ")
assert test_session1 == "IPC1"
def test_2(test_session1, test_session2):
print("func test_2 in test_session.py ")
assert test_session2 == "IPC2"
class Test_module():
def test_module1(self, test_session1, test_session2):
print(" class test_sesson1 ")
assert test_session1 == "IPC1"
assert test_session2 == "IPC2"
def test_module2(self, test_session1, test_session2):
print(" class test_sesson2 ")
assert test_session1 == "IPC1"
assert test_session2 == "IPC2"
运行结果:
test_session.py
test_session1 in conftest.py
.func test_1 in test_session.py
test_session2 conftest.py
.func test_2 in test_session.py
. class test_sesson1
. class test_sesson2
可以看出,在test_session.py文件中可以调用conftest.py中装饰器参数为scoope=‘session’的fixture函数,而且和’class’、'module’相似,带有scope='session’的fixture函数在被所有测试用例函数调用前,只执行一次。
@pytst.mark.usefixtures()的作用
@pytst.mark.usefixtures()标记需要执行的测试用例, 但是使用它无法获取到被调用函数的返回值;而不使用标记,同时被调用的函数被类似于@pytest.fixtures()装饰时,测试用例函数可以通过调用fixture函数,获取返回值。另外,这个装饰器是写在要执行的测试用例函数上面的,而类似于@pytest.fixture(scope=‘function’)是写在被调用的fixture函数上面,以供测试用例函数调用。
当测试用例不需要返回值时,可以考虑使用@pytst.mark.usefixtures(),两者都行;测试用例需要返回值时只能使用@pytest.fixture()来调用。
import pytest
@pytest.fixture()
def test_1():
print("\n this is test_1")
@pytest.mark.usefixtures('test_1')
def test_markusefixtures():
print("this is test_markusefixtures")
@pytest.mark.usefixtures('test_1')
class Test_markusefixtures():
def test_1(self):
print("Test_markusefixtures1")
def test_2(self):
print("Test_markusefixtures2")
运行结果:
this is test_1
.this is test_markusefixtures
this is test_1
.Test_markusefixtures1
this is test_1
.Test_markusefixtures2
多个@pytst.mark.usefixtures()作用于同一个测试用例
当需要多个@pytst.mark.usefixtures()装饰一个测试用例时,可以直接在测试用例代码上面添加,不过放置底层的装饰器先执行,最上层的装饰器最后执行。
import pytest
@pytest.fixture()
def test_1():
print("\n this is test_1")
@pytest.fixture()
def test_2():
print("\n this is test_2")
@pytest.mark.usefixtures('test_1')
@pytest.mark.usefixtures('test_2')
class Test_markusefixtures():
def test_mark1(self):
print("Test_markusefixtures1")
def test_mark2(self):
print("Test_markusefixtures2")
运行结果
this is test_2
this is test_1
.Test_markusefixtures1
this is test_2
this is test_1
.Test_markusefixtures2
autouse 参数的作用
在fixture的参数中,autouse默认值为False。当autouse为True时,测试用例会自动调用被@pytest.fixture(autouse=‘True’)装饰的函数。当一个项目或者脚本中存在很多基础的函数需要被调用,那么使用autouse=True会比较方便,省去了测试用装饰器中的传参。
import pytest
@pytest.fixture(scope='function', autouse=True)
def test_function_scope():
print("\n I'm function_scope, I need to be called forever.")
@pytest.fixture(scope='class', autouse=True)
def test_class_scope():
print("\n I'm class_scope, I need to be called forever.")
@pytest.fixture(scope='module', autouse=True)
def test_module_scope():
print("\n I'm module_scope, I need to be called forever.")
def test_1():
print("test_1: I will call ... ")
class Test_autouse():
def test_2(self):
print("test_2: I will call ... ")
def test_3(self):
print("test_3: I will call ... ")
运行结果:
I'm module_scope, I need to be called forever.
I'm class_scope, I need to be called forever.
I'm function_scope, I need to be called forever.
.test_1: I will call ...
I'm class_scope, I need to be called forever.
I'm function_scope, I need to be called forever.
.test_2: I will call ...
I'm function_scope, I need to be called forever.
.test_3: I will call ...