fixture是pytest特有的功能,它用@pytest.fixture标识,定义在函数前面。在编写测试函数的时候,可以将此函数的名称作为传入参数,pytest会以依赖注入方式将该函数的返回值作为测试函数的传入参数。
fixture主要的目的是为了提供一种可靠和可重复性的手段去运行那些最基本的测试内容。例如在测试网站的功能时,每个测试用例都要登录和退出,利用fixture就可以只执行一次,否则每个测试用例都要执行这两步也是冗余。
应用:
-
登录登出功能
-
数据初始化
语法
fixture(scope="function", params=None, autouse=False, ids=None, name=None)
- scope 作用范围
fixture的scope有5个级别参数function(默认)、class、module、package和session。package被认为是实验性的。
·function:每个函数或方法都会调用;
·class:每个类调用一次,一个类可以有多种方法;
·module:每个.py文件调用一次,该文件内又有多个function和class;
·Session:一个session调用一次
- params:一个可选的参数列表
params 以可选的参数列表形式存在。在测试函数中使用时,可通过 request.param 接收设置的返回值(即 params 列表里的值)。params 中有多少元素,在测试时,引用此 fixture 的函数就会调用几次。
-
autouse:是否自动执行****设置的 fixtures
当 autouse 为 True 时,测试函数即使不调用 fixture 装饰器,定义的 fixture 函数也会被执行。
-
ids:指定每个字符串 id
当有多个 params 时,针对每一个 param,可以指定 id,这个 id 将变为测试用例名字的一部分。如果没有提供 id,则 id 将自动生成。
-
name:fixture 的名称
name 是 fixtures 的名称, 它默认是你装饰的那个 fixture 函数的名称。你可以通过 name 参数来更改这个 fixture 名称,更改后,如果这个 fixture 被调用,则使用你更改过的名称即可。
调用方法
如果每次使用fixture都要通过传参的方式,则应改变原来测试方法的结构。如何不通过注入的方式让测试方法执行呢?有2种方式可选,第一种在fixture的参数中将autouse参数设置为True,这样便会自动应用所作用的范围。第二种使用@pytest.mark.usefixtures,在需要的测试方法上添加
通过fixture函数名调用
import pytest
# 首先, 在fixture函数上,加@pytest.fixture()
@pytest.fixture()
def my_method():
print('This is itesting Speaking')
# 其次,把fixture函数的函数名作为参数,传入被测试用例
def test_use_fixtures(my_method):
print('Please follow iTesting from wechat')
#输出 'This is itesting Speaking'
#'Please follow iTesting from wechat'
通过 usefixtures 装饰器使用
# 在lagouAPITest项目下新建一个文件,命名为test_fixture_usage.py
import pytest
@pytest.fixture()
def my_method():
print('This is iTesting Speaking')
# 函数直接使用fixture
@pytest.mark.usefixtures('my_method')
def test_use_fixtures():
print('Please follow iTesting from wechat')
class TestClass1:
# 类方法使用fixture
@pytest.mark.usefixtures('my_method')
def test_class_method_usage(self):
print('[classMethod]Please follow iTesting from wechat')
# 类直接使用fixture 类中的每个方法都会调用一次
@pytest.mark.usefixtures('my_method')
class TestClass2:
def test_method_usage_01(self):
pass
def test_method_usage_02(self):
pass
通过 autouse 参数隐式使用
autouse为True时,每个text函数默认调用fixture
import pytest
# 首先, 在fixture函数上,加@pytest.fixture()
@pytest.fixture(autouse=True)
def my_method():
print('This is itesting Speaking')
# 其次,把fixture函数的函数名作为参数,传入被测试用例
def test_use_fixtures():
print('Please follow iTesting from wechat')
fixture带参数
import pytest
@pytest.fixture(params=['hello', 'iTesting'])
def my_method(request):
return request.param
def test_use_fixtures_01(my_method):
print('\n this is the 1st test')
print(my_method)
@pytest.mark.usefixtures('my_method')
def test_use_fixtures_02():
print('\n this is the 2nd test')
# 注意,如果我在这里想通过print(my_mthod)来打印出fixuture提供的参数,是不行的, 因为使用usefixtures无法获取fixture的返回值,如需要fixture的返回值,则需用test_use_fixtures_01那样的调用方式
conftest.py 来共享 fixture
在 conftest.py 中定义的 fixture 不需要进行 import,pytest 会自动查找使用。 pytest 查找 fixture 的顺序是首先查找测试类(Class),接着查找测试模块(Module),然后是 conftest.py 文件,最后是内置或者第三方插件
在根目录创建conftest.py
import pytest
@pytest.fixture()
def login():
print("这是登录")
yield
print("这是退出登录")
@pytest.fixture(autouse=True)
def register():
print("这是注册")
test_demo
def test(login):
print("this is a test")
运行test_demo,可以看到先调用了注册接口,因为该fixture的autouse是True。
通过函数名调用login 输出yield之前的语句,在该test执行后,输出了yield之后的语句。
在 pytest 的 fixture 里,yield关键字语句之前的属于 set up,而 yield 以后的语句属于 tear down。
所有的测试前置及后置功能均可以定义在 conftest.py 文件中,供整个测试使用,而不必在每一个测试类中定义
fixture 与parametrize 结合实现参数化
如果测试数据需要在 fixture 方法中使用,同事也需要在用例中使用,可以让 parametrize 的 indirect 参数为 True
def parametrize(self, argnames, argvalues, indirect=False, ids=None, scop=None):
indirect = True,pytest 会把 argnames 当做函数执行,将 argvalues 作为参数传入到 argnames
import pytest
# 方法名作为参数
test_user_data = ['Tom', 'Jerry']
@pytest.fixture(scope='module')
def login_r(request):
# 通过 request.param 获取参数
user= request.param
print(f"/n 登录用户: {user}")
return user
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
a = login_r
print(f"用例中 login 的返回值; {a}")
assert a != ""
执行结果:
综上,当 indirect = True 时,会将 login_r 作为参数,test_user_data 作为参数传入 login_r 中,生成多条测试数据。通过 return 将结果返回。当调用 login_r 可以获取到 login_r 这个方法的返回数据。
典型使用场景-登录登出
最典型的使用场景:登录功能,有些功能需要登录才能使用,例如支付功能和查看购物车,而浏览商品功能不需要登录。
fixture实现步骤如下:
(1)导入pytest。
(2)创建login()函数。
(3)在login()函数上加@pytest.fixture()。
(4)在要使用的测试方法中传入(登录函数名称),也就是先执行login()函数再执行本测试方法。
(5)不传入参数表明不需要登录,此时可以直接执行测试方法。
import pytest
@pytest.fixture()
def login():
print("用户名登录")
def test_cart(login):
print("登录后查看购物车")
def test_find_goods():
print("不登录浏览商品")
def test_pay(login):
print("登录后执行支付")
if __name__ == '__main__':
pytest.main(['-s','test.py'])
如图,在test_cart里,调用了login,因为查看购物车需要先登录。当测试用例执行时,会先执行login,将输出结果传给test_cart,然后执行test_cart.
典型使用场景-模块级别数据清理
测试方法后需要销毁并清除的数据该如何处理呢?可以用tesrdownClass,这里介绍用fixture方法。
通过在同一模块中加入yield关键字,当执行fixture时,第一次执行至yield,当整个模型的用例都执行结束后,继续执行fixture内的yield之后的语句。
步骤如下:(1)添加@pytest.fixture(scope=module)语句。
(2)在登录的方法中添加yield,之后添加销毁清除的步骤。
import pytest
@pytest.fixture(scope="module")
def open():
print("open broswer,open baidu")
yield
print("close broswer")#teardown
def test_cart():
print("登录后查看购物车")
def test_find_goods(open):
print("不登录浏览商品")
def test_pay():
print("登录后执行支付")
if __name__ == '__main__':
pytest.main(['-s','test.py'])
可以看到,第二个test里调用了fixture,在该test前执行了fixture,在最后执行fixture中yield之后的语句。