前言
虽然 setup 和 teardown 可以执行一些前置和后置操作,但是这种是针对整个脚本全局生效的
如果有以下场景:1.用例一需要执行登录操作;2.用例二不需要执行登录操作;3.用例三需要执行登录操作,则setup和teardown则不满足要求。
fixture 可以让自定义测试用例的前置条件
fixture 的优势
命名方式灵活,不限于 setup 和 teardown 两种命名
conftest.py
可以实现数据共享,不需要执行 import 就能自动找到 fixture
scope=module
,可以实现多个 .py
文件共享前置
scope=“session
” 以实现多个 .py
跨文件使用一个 session 来完成多个用例
fixture 参数 fixture 修饰器来标记固定的工厂函数,在其他函数,模块,类或整个工程调用它时会被激活并优先执行,通常会被用于完成预置处理和重复操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 def fixture(scope ="function" , params =None, autouse =False , ids =None, name =None) 参数: scope:被标记方法的作用域,可选四组参数 "function" (default):作用于每个测试方法,每个test都运行一次 "class" :作用于整个类,每个class的所有test只运行一次 "module" :作用于整个模块,每个module的所有test只运行一次 "session:作用于整个session(慎用),每个session只运行一次,即整个测试会话,即开始执行 Pytest 到结束测试 默认取值为 function(函数级别),控制范围的排序为:`session > module > class > function` params:(list类型)提供参数数据,供调用标记方法的函数使用 autouse:是否自动运行,默认为False不运行,设置为True自动运行 ids: 每个参数对应的字符串id列表,因此它们是测试id的一部分。如果没有提供id,它们将从参数中自动生成。 name: fixture的名称。 这默认为装饰函数的名称。 如果fixture在定义它的同一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽; 解决这个问题的一种方法是将装饰函数命名 “fixture_ <fixturename>”然后使用”@ pytest.fixture(name ='<fixturename>')”。
如何调用 fixture 执行的三种方式:
将 fixture 名称作为测试用例的输入参数
使用装饰器 pytest.mark.usefixtures(fixturename)
fixture 设置 autouse=True
扩展知识点:
在类声明上面加 @pytest.mark.usefixtures()
,代表这个类里面所有测试用例都会调用该 fixture
可以叠加多个 @pytest.mark.usefixtures()
,先执行的放底层,后执行的放上层
可以传多个 fixture 参数,先执行的放前面,后执行的放后面
如果 fixture 有返回值,用 @pytest.mark.usefixtures()
是无法获取到返回值的,必须用传参的方式(方式一)
将fixture名称作为测试用例的输入参数 1 2 3 4 5 6 7 8 9 class Test_ABC : @pytest.fixture() def before (self) : print("------->before" ) def test_a (self,before) : print("------->test_a" ) assert 1 if __name__ == '__main__' : pytest.main("-s test_abc.py" )
1 2 3 4 执行结果: test_abc.py ------->before # 发现before 会优先于测试函数运行 ------->test_a
使用装饰器 pytest.mark.usefixtures(fixturename)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import pytest@pytest.fixture() # fixture标记的函数可以应用于测试类外部 def before () : print("------->before" )@pytest.mark.usefixtures("before") class TestOne : def setup (self) : print("------->setup" ) def test_a (self) : print("------->test_a" ) assert 1 def test_b (self) : print("------->test_b" ) assert 1 if __name__ == '__main__' : pytest.main("-s test_one.py" )
1 2 3 4 5 6 7 8 9 10 执行结果: test_one.py::TestOne::test_a ------->before ------->setup ------->test_a PASSED test_one.py::TestOne::test_b ------->before ------->setup ------->test_b PASSED
fixture设置 autouse=True
1 2 3 4 5 6 7 8 9 10 11 12 import pytest@pytest.fixture(autouse=True) # 设置为默认运行 def before () : print("------->before" )class Test_ABC : def setup (self) : print("------->setup" ) def test_a (self) : print("------->test_a" ) assert 1 if __name__ == '__main__' : pytest.main("-s test_abc.py" )
1 2 3 4 5 执行结果: test_abc.py ------->before # 发现before 自动优先于测试类运行 ------->setup ------->test_a
fixture的返回值 1 2 3 4 5 6 7 8 9 10 11 12 13 import pytest@pytest.fixture() def need_data () : return 2 class Test_ABC : def test_a (self,need_data) : print("------->test_a" ) assert need_data != 3 if __name__ == '__main__' : pytest.main("-s test_abc.py" )
1 2 3 4 5 6 7 8 9 10 11 12 import pytest@pytest.fixture(params=[1, 2, 3]) def need_data (request) : return request.param class Test_ABC : def test_a (self,need_data) : print("------->test_a" ) assert need_data != 3 if __name__ == '__main__' : pytest.main("-s test_abc.py" )
1 2 3 4 5 6 7 8 9 10 11 12 执行结果: test_abc.py 1 . 2 . 3 F
fixture 使用 yield 实现teardown 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import pytest@pytest.fixture(scope="session") def open () : print("===打开浏览器===" ) test = "test" yield test print("==关闭浏览器==" )@pytest.fixture def login (open) : print(f"open方法yield的返回结果:{open} " ) name = "==我是账号==" pwd = "==我是密码==" yield name, pwd print("登录成功" )def test_s1 (login) : print("==用例1==" ) print(login) name, pwd = login print(name, pwd) assert "账号" in name assert "密码" in pwddef test_s2 (login) : print("==用例2==" ) print(login)if __name__ == '__main__' : pytest.main("-s test_one.py" )
1 2 3 4 5 6 7 8 9 10 11 12 13 执行结果: test_one.py::test_s1 ===打开浏览器=== open方法yield 的返回结果:test ==用例1 == ('==我是账号==' , '==我是密码==' ) ==我是账号== ==我是密码== PASSED登录成功 test_one.py::test_s2 open方法yield 的返回结果:test ==用例2 == ('==我是账号==' , '==我是密码==' ) PASSED登录成功 ==关闭浏览器==
yield和with结合使用 1 2 3 4 5 @pytest.fixture(scope="module") def smtp_connection () : with smtplib.SMTP("smtp.gmail.com" , 587 , timeout=5 ) as smtp_connection: yield smtp_connection
该 smtp_connection 连接将测试完成执行后已经关闭,因为smtp_connection 对象自动关闭时, with 语句结束
fixtrue 参数 request 传单个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import pytest@pytest.fixture() def pre_data (request) : name = request.param print(f"== 账号是:{name} ==" ) return name data = ["pyy1" , "polo" ] ids = [f"pre_test_name is:{name} " for name in data]@pytest.mark.parametrize("pre_data", data, ids=ids, indirect=True) def test_name (pre_data) : print(f" 测试用例的登录账号是:{pre_data} " )
多个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @pytest.fixture() def pre_data (request) : param = request.param print(f"账号是:{param['username' ]} ,密码是:{param['pwd' ]} " ) return param data = [ {"username" : "name1" , "pwd" : "pwd1" }, {"username" : "name2" , "pwd" : "pwd2" }, ]@pytest.mark.parametrize("pre_data", data, indirect=True) def test_name_pwd (pre_data) : print(f"账号是:{pre_data['username' ]} ,密码是:{pre_data['pwd' ]} " )
多个fixture(只加一个装饰器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @pytest.fixture(scope="module") def input_user (request) : user = request.param print("登录账户:%s" % user) return user@pytest.fixture(scope="module") def input_psw (request) : psw = request.param print("登录密码:%s" % psw) return psw data = [ ("name1" , "pwd1" ), ("name2" , "pwd2" ) ]@pytest.mark.parametrize("input_user,input_psw", data, indirect=True) def test_more_fixture (input_user, input_psw) : print("fixture返回的内容:" , input_user, input_psw)
多个fixture(叠加装饰器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @pytest.fixture(scope="function") def input_user (request) : user = request.param print("登录账户:%s" % user) return user@pytest.fixture(scope="function") def input_psw (request) : psw = request.param print("登录密码:%s" % psw) return psw name = ["name1" , "name2" ] pwd = ["pwd1" , "pwd2" ]@pytest.mark.parametrize("input_user", name, indirect=True) @pytest.mark.parametrize("input_psw", pwd, indirect=True) def test_more_fixture (input_user, input_psw) : print("fixture返回的内容:" , input_user, input_psw)