fixture (使用pytest.fixture 替换使用setup, yield替换使用teardown,并将前置条件写进conftest.py文件)
fixture (是pytest特有的功能)
# fixture 官方文档:: https://docs.pytest.org/en/stable/fixture.html
1、fixture特点:
必须用pytest.fixture装饰器装饰;fixture有明确的名字,在其他函数(function,默认),模块(module),类(class)或整个工程(session)调用它时会被激活,pytest自己去查找使用;
可以调用其他的fixture;
可以和测试函数写在同一个文件里;
fixture也可作为是预置项,在测试用例执行之前需要去配置它,执行完后去释放它;
fixture还提供了参数化功能,根据配置和不同组件来选择不同的参数;
查看fixture帮助源码:
fixture(scope="function", params=None, autouse=False, ids=None, name=None):
"""使用装饰器标记fixture的功能 可以使用此装饰器(带或不带参数)来定义fixture功能。 fixture功能会在运行测试之前调用它:test模块或类可以使用pytest.mark.usefixtures(fixturename标记) 测试功能可以直接使用fixture名称作为输入参数,在这种情况下,夹具实例从fixture返回功能将被注入。 """ :scope: scope 有四个级别参数 "function" (默认), "class", "module" or "session". :params: 一个可选的参数列表,它将导致多个参数调用fixture功能和所有测试使用它 :autouse: 如果为True,则为所有测试激活fixture func 可以看到它。 如果为False(默认值)则显式需要参考来激活fixture :ids: 每个字符串id的列表,每个字符串对应于params 这样他们就是测试ID的一部分。 如果没有提供ID它们将从params自动生成 :name: fixture的名称。 这默认为装饰函数的名称。 如果fixture在定义它的同一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽; 解决这个问题的一种方法是将装饰函数命名。“fixture_ <fixturename>”然后使用”@ pytest.fixture(name ='<fixturename>')“”。
根据上述fixture介绍的总结:
fixture的参数scope可传递的4种类型:
function:每个函数及方法都运行,默认是function的scope
class:每个测试类class之前执行一次
module:每个.py文件 只运行一次
session:每个session只运行一次(即可以跨.py文件运行)
2、使用firture相对于setup和teardown的优缺点:
命名方式灵活,不局限于setup和teardown名称命名
conftest.py 配置里可以实现数据共享,不需要import就能自动找到一些配置
scope="module" 可以实现共享前置条件, 每一个.py文件调用一次
scope="session" 以实现多个.py跨文件使用一个session来完成多个用例(也就是当我们有多个.py文件的用例时候,
如果多个用例只需调用一次fixture,那就可以设置为scope="session",并且写到conftest.py文件里)
3、Fixture的使用
一般是来装饰用例执行前的预置条件的,并且一般是将装饰的这个函数写进配置文件conftest.py
(这个配置文件通常这样命名,并且可以存在多个的,但是同一个目录里面只能一个,记住了:这个配置文件也只针对存放的这个目录生效!!!)
为什么要这样使用:
比如在:
同一个.py文件里面,或者不同的.py文件内,一个用例需要登录,另外的用例不需要登录,那么登录方法在这种情况下就是前置条件了,那么这个时候使用setup是办不到的。
解决方法:利用fixture将登录方法写入到conftest.py配置文件内,然后其他需要用到的时候直接调用,不需要的就不调用。这就很方便了!
conftest.py配置文件 需要注意的是:
conftest.py 脚本名称是固定的不可修改
conftest.py 和运行的用例要在同一个pakage下,并且那个package下存在有__init__.py文件,不能把它删除了
不需要import导入 conftest.py,当pytest用例运行时会自动查找
4、实际应用:
1、@pytest.fixture()-----------------类似setup
2、@pytest.fixture(scope='module\class\session')---类似setup
3、yield ----------------------------类似teardown
4.1、当在相同一个.py文件内,调用相同方法时:(处于同一文件中,直接对要调用的方法,进行使用@pytest.fixture()装饰器,然后将就可以了。)
新建:test_demo.py
import pytest @pytest.fixture() #@pytest.fixture()里面没有参数,此时默认scope="function",此时针对函数有效 def login(): #fixture装饰的函数,是可以有return 返回值的--使用yeild来接收返回值,后面详细说,如果没有return默认是None。 print("这是登录方法") def test_case1(login): #需要登录后才能执行这个用例,那么直接传入login方法的名称即可 print("这是查询功能") def test_case2(): # 不传login print("这是搜索功能")
def test_case3(login):
print("这是搜索功能") if __name__ == "__main__": pytest.main(["-v", "test_demo.py"])
------>>执行结果可见:
同一.py文件中,遇到有些功能需要先登录,有的不需要,那么这个时候就可以使用@pytest.fixture()装饰登录函数,将登录变成前置条件,
当有需要使用登录的函数方法时,直接将登录函数名称传入,即相当于调用了。(那么执行结果就是先调用登录函数方法,然后执行其他函数方法)
4.2、当有多个.py文件需要调用相同的前置功能时,那么就可以建一个配置文件conftest.py,
然后pytest运行用例时会默认读取conftest.py里面的配置方法
新建配置文件(注意名称): conftest.py-------->>必须为这个!否则pytest找不到
import pytest
@pytest.fixture() #不传参数时默认是@pytest.fixture(scope="function")
def login():
print("这是登录功能")
新建test_demo1.py: import pytest def test_case1(login): #传入login print("用例1") def test_case2(): # 不传login print("用例2") if __name__ == "__main__": pytest.main(["-v", "test_demo1.py"])
新建test_demo2.py:
import pytest def test_case4(login): #传入login print("用例4") def test_case5(): # 不传login print("用例5") if __name__ == "__main__": pytest.main(["-v", "test_demo2.py"])
结果可见:
将login函数写到了配置文件中,且使用@pytest.fixture装饰login函数;
然后单独去运行demo1.py或者demo2.py,都可以实现分别去调用这个写进了配置文件conftest.py的前置的 登录方法
同上面的 例4.1 是@pytest.fixture( ) 不传参数的操作,
如果传参数呢?接下来请看:
@pytest.fixture(scope='module') #module作用是当前整个.py文件都会生效,用例调用时,直接将函数名传入当做参数来使用并调用,并且只调用一次
例1:新建test_demo.py:(当都调用相同的操作方法时) import pytest @pytest.fixture(scope="module") def open(): print("打开浏览器访问至百度首页") def test_case1(open): print("这是查询") def test_case2(open): print("用这是搜索") if __name__ == "__main__": pytest.main(["-s", "test_demo.py"]) 运行结果会看见:2个用例都调用了open,open()方法会在执行用例前 运行一次
例2:新建test_demo.py: (当一个用例调用,一个用例不调用时)
import pytest
@pytest.fixture(scope="module")
def open():
print("打开浏览器访问至百度首页")
def test_case1(): #不引用
print("用例1")
def test_case2(open): #case2引用open函数
print("用例2")
if __name__ == "__main__":
pytest.main(["-s", "test_demo.py"])
结果open只会在case2之前运行一次。(因为只case2 使用了open函数)
4.3、yield 实现结束操作(同teardown)
新建test_demo.py: import pytest @pytest.fixture(scope="module") def open(): print("打开浏览器,访问至百度首页") yield #建议!!!:编写用例时,yield一般放前置方法里面,注意yield的位置,是写在结束操作之前,且所有用例执行完之后才执行一次 print("这是teardown操作") print("关闭浏览器") def test_case1(open): print("用例1") def test_case2(open): print("用例2") if __name__ == "__main__": pytest.main(["-v", "test_demo.py"]) 结果:因为@pytest.fixture(scope="module"),module是只针对当前.py模块文件生效的,
yield一般是放在前置方法里面的(即被@pytest.fixture()装饰过的函数方法)
并且用例里面都调用了open()操作,在所有用例执行前执行一次open,然后运行用例,最后所有用例执行完之后,才执行一次yield后面的结束操作 注:yield在用例里面充当了teardown操作。就算用例执行报错,yield还是会正常执行不会被影响