pytest测试用例编写规则:
文件名以test_.py文件和_test.py
以test_开头的函数
以Test开头的类
以test_开头的方法
注:执行命令时使用-s参数可以将执行用例中的print打印函数,如没有使用,pytest用例中的print函数将不会被执行
pytest -s test_fixture.py
pytest --fixtures xx.py 可以查看某文件里的fixture
@pytest.fixture()
def creat_id():
a=2
return a*3
def test_compute(creat_id):
some=creat_id+4
assert some==11
creat_id = 6
def test_compute(creat_id):
some=creat_id+4
> assert some==11
E assert 10 == 11
test_study.py:42: AssertionError
在这里故意用例执行失败,通过回溯可以看到
1,test_compute测试函数需要一个名为的函数参数creat_id,通过查找名为的带有fixture标记的函数,可以找到匹配的夹具函数creat_id
2,creat_id() 被称为创建实例,得到creat_id=6
3,test_compute函数开始执行,断言失败
当fixture标记的函数没有返回值时,执行用例,改fixture标记函数返回值默认为None
creat_id = None
def test_compute(creat_id):
> some=creat_id+4
E TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
test_study.py:41: TypeError
如果想要实现该creat_id函数能够被其他模块/类等共享使用,可以把写入到conftest.py文件,并写上scope对应的值,对于可能的值scope有:function,class,module,package或session。不填写默认function
1.如果用例需要用到多个fixture的返回数据,fixture也可以返回一个元祖,list或字典,然后从里面取出对应数据。
@pytest.fixture()
def creat_id():
return {
'a':2*3,'b':2+3}
def test_compute(creat_id):
some=creat_id['a']+4
some2 = creat_id['b'] + 4
assert some==11
assert some2 == 11
执行用例,可以看到creat_id得到一个字典,然后再分别取值
creat_id = {
'a': 6, 'b': 5}
def test_compute(creat_id):
some=creat_id['a']+4
some2 = creat_id['b'] + 4
> assert some==11
E assert 10 == 11
test_study.py:41: AssertionError
2.也可以分成多个fixture,然后在用例中传多个fixture参数
@pytest.fixture()
def creat_id():
return 2*3
@pytest.fixture()
def creat_id2():
return 2+3
def test_compute(creat_id,creat_id2):
some=creat_id+4
some2 = creat_id2 + 4
assert some==11
assert some2 == 11
执行用例,可以看到creat_id = 6, creat_id2 = 5
creat_id = 6, creat_id2 = 5
def test_compute(creat_id,creat_id2):
some=creat_id+4
some2 = creat_id2 + 4
> assert some==11
E assert 10 == 11
test_study.py:46: AssertionError
将定义为fixture的方法的返回值用在其他方法中
fixture里面有个scope参数可以控制fixture的作用范围:
@pytest.fixture(scope='function')
session>module>class>function
-function:每一个函数或方法都会调用,默认情况下是function
-class:每一个类调用一次,一个类中可以有多个方法
-module:每一个.py文件调用一次,该文件内又有多个function和class
-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module
注:
fixture为session级别是可以跨.py模块调用的,也就是当我们有多个.py文件的用例的时候,如果多个用例只需调用一次fixture,那就可以设置为scope=“session”,并且写到conftest.py文件里。
conftest.py文件名称时固定的,pytest会自动识别该文件。放到项目的根目录下就可以全局调用了,如果放到某个package下,那就在该ackage内有效
scope越大,实例化越早
1.当函数调用多个fixtures的时候,scope较大的(比如session)实例化早于scope较小的(比如function或者class)
2.同样scope的顺序则按照其在测试函数中定义的顺序及依赖关系来实例化
fixture源码详解
fixture(scope=‘function’,params=None,autouse=False,ids=None,name=None):
scope:有四个级别参数"function"(默认),“class”,“module”,“session”
params:一个可选的参数列表,它将导致多个参数调用fixture功能和所有测试使用它。
autouse:如果True,则为所有测试激活fixture func可以看到它。如果为False则显示需要参考来激活fixture
ids:每个字符串id的列表,每个字符串对应于params这样他们就是测试ID的一部分。如果没有提供ID它们将从params自动生成
name:fixture的名称。这默认为装饰函数的名称。如果fixture在定义它的统一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽,解决这个问题的一种方法时将装饰函数命令"fixture_“然后使用”@pytest.fixture(name=’’)"。
1.函数或类里面方法直接传fixture的函数参数名称@pytest.fixture()
@pytest.fixture()
def test1():
print('\n开始执行function')
def test_a(test1):
print('---用例a执行---')
#执行结果
开始执行function
---用例a执行---
2.使用装饰器@pytest.mark.usefixtures()修饰需要运行的用例,该方式获取不到fixture标识函数的返回值
如:@pytest.mark.usefixtures('test1')
@pytest.fixture()
def test1():
print('\n开始执行function')
@pytest.mark.usefixtures('test1')
def test_a():
print('---用例a执行---')
#执行结果
开始执行function
---用例a执行---
usefixtures与传fixture区别
usefixture,无法获取到返回值。传fixture,可以获取到返回值。
所以当fixture需要用到return出来的参数时,只能使用传fixture方式,当不需要用到return出来的参数时,两种方式都可以。
3.叠加usefixtures
如果一个方法或者一个class用例想要同时调用多个fixture,可以使用
@pytest.mark.usefixtures()进行叠加。
注意叠加顺序,先执行的放底层,后执行的放上层。
如:先执行test2 再执行test1
@pytest.mark.usefixtures('test1')
@pytest.mark.usefixtures('test2')
def test_xx(): #测试用例
pass
4.autouse设置为True,自动调用fixture功能
设置scope为module级别,在当前.py用例模块只执行一次,autouse=True自动使用
设置scope为function级别,每个用例前都调用一次,自动使用
如:@pytest.fixture(scope="module", autouse=True)
可以通过使用yield语句代替return,fixture里面的teardown用yield来唤醒teardown的执行,yield语句之后的所有代码都将用作拆卸代码
@pytest.fixture(scope="module")
def open():
print("\n1,打开浏览器,并且打开百度首页") #实现setup
yield 10 #“yield 10”替代“return 10”,且实现teardown
print("\n2,执行teardown!")
print("3,最后关闭浏览器")
def test_compute2(open):
assert open==10
def test_compute3(open):
assert open==9
执行时加上“-s -q”命令,可以发现,scope="module"代表每个模块调用一次,调用时先执行open中的第一行打印代码,然后yield 10
返回数值9给测试函数使用,当两个测试函数都执行完毕后(一个成功一个失败),再执行yield后面的两行打印代码
1,打开浏览器,并且打开百度首页
.F
2,执行teardown!
3,最后关闭浏览器
yield遇到异常:
1.如果其中一个用例出现异常,不影响yield后面的teardown执行,运行结果互不影响,并且在用例全部执行完之后,会呼唤teardown的内容
2.如果在setup就异常了,那么是不会去执行yield后面的teardown内容了
与在@ pytest.mark.parametrize中使用的方式相同,用于在参数化灯具的值集中应用标记。在fixture中使用params参数
是一个可选的参数列表,它将导致多个参数调用fixture功能和所有测试使用它。如:
@pytest.fixture(params=[0, 1)
然后被fixture标记的函数需要使用request中的request.param
才能使用到params中的值
1,
@pytest.fixture(params=[3,4,5, pytest.param(2, marks=pytest.mark.skip)])
def creat_id(request):
print('打印param的值',request.param)
return request.param+3
def test_compute(creat_id):
assert creat_id==6
执行时加‘-v’命令,可以看到每次测试函数的情况,
运行此测试将跳过对creat_id函数中value 的调用“2”,最后test_compute一共执行了3次,分别使用了creat_id函数的三个值,每次得到一个参数值;跳过1条
test_study.py::test_compute[3] 打印param的值 3
PASSED
test_study.py::test_compute[4] 打印param的值 4
FAILED
test_study.py::test_compute[5] 打印param的值 5
FAILED
test_study.py::test_compute[2] SKIPPED
1.一个工程下可以建多个conftest.py的文件,一般在工程根目录下设置的conftest文件起到全局作用。在不同子目录下也可以放conftest.py的文件,作用范围只能在该层级以及以下目录生效。
2.conftest在不同的层级间的作用域不一样,再执行某模块时,先执行这个模块对应的祖先目录是否有conftest文件,然后依次往下级目录查找
3.conftest是不能跨模块调用的
4.conftest.py与运行的用例要在同一个pakage下,并且有init.py文件
注意:
Pytest对于每个fixture只会缓存一个实例,这意味着如果使用参数化的fixture,pytest可能会比定义的作用域更多次的调用fixture函数(因为需要创建不同参数的fixture)
可以使用pytest --fixtures xx.py
来查看可用的fixture
在相对较大的测试套件中,很可能需要override一个global或root一个已locally 定义的夹具,以保持测试代码的可读性和可维护性。
1,在文件夹级别(通过conftest文件)重写fixtures方法
如在/test1/conftest.py
和/test1/test2/conftest.py
中都有一个被fixture标识的同名方法:
则/test1/
路径下的测试模块使用conftest.py
中的的fixture方法,
而/test1/test2/
路径下的测试模块会使用fixture.py
文件中重写的fixture方法
test1/
__init__.py
conftest.py
#test1/ conftest.py
import pytest
@pytest.fixture
def username():
return 'username'
test_something.py
# test1/test_something.py
def test_username(username):
assert username == 'username'
test2/
__init__.py
conftest.py
# test1/test2/conftest.py
import pytest
@pytest.fixture
def username(username):
return 'overridden-' + username
test_something.py
# test1/test2/test_something.py
def test_username(username):
assert username == 'overridden-username'
2,在测试模块级别重写fixtures方法
如在/test1/conftest.py
都有一个被fixture标识的同名方法:username()
/test1
路径下有一测试模块test_something.py,重写了fixtures方法1:username()
/test1
路径下有一测试模块test_something_else.py,重写了fixtures方法2:username()
这两个文件分别使用自己的fixtures方法:username()
test1/
__init__.py
conftest.py
# test1/conftest.py
import pytest
@pytest.fixture
def username():
return 'username'
test_something.py
# test1/test_something.py
import pytest
@pytest.fixture
def username(username):
return 'overridden-' + username
def test_username(username):
assert username == 'overridden-username'
test_something_else.py
# test1/test_something_else.py
import pytest
@pytest.fixture
def username(username):
return 'overridden-else-' + username
def test_username(username):
assert username == 'overridden-else-username'
总结:
fixtures方法的查找方法
先从根目录下的fixture.py
文件中查找,
依次往次级目录的fixture.py
文件中查找,
最后再查找当前测试模块py文件中是否有该fixtures方法,使用最近的一个fixtures方法
注:同一个py文件中可以有同名的fixtures方法,也是使用最近的一个
参考地址:https://www.cnblogs.com/huizaia/p/10331469.html
传入参数参考地址:https://blog.csdn.net/BearStarX/article/details/101197886