python之pytest_addoption : 命令行参数
场景
1.一般公司测试环境都有多套,测试的时候我们需要在不同的环境下进行
2.在自动化执行时,在不同的环境下我们要指定不同的系统配置,每次修改框架代码配置这个很不自动化
3.pytest_addoption注册参数 这个就很好的解决了这个问题,它能在执行命令的时候传递参数
二、@pytest.fixture()函数的介绍
2.1 pytest.fixture()函数介绍
fixture是pytest的核心功能,也是亮点功能;
fixture的目的是提供一个固定基线,在该基线上测试可以可靠地和重复地执行。fixture提供了区别于传统单元测试(setup/teardown)有显著改进:
(1)有独立的命名,并通过声明它们从测试函数、模块、类或整个项目中的使用来激活;
(2)按模块化的方式实现,每个fixture都可以相互调用;
(3)fixture的范围从简单的单元扩展到复杂的功能测试,允许根据配置和组件选项对fixture和测试用例进行参数化,或者跨函数function,类class,模块module或整个测试会话session范围。
Fixture参数详解及使用
@pytest.fixture(scope = "function",params=None,autouse=False,ids=None,name=None) 参数详解: 1、SCOPE 用于控制Fixture的作用范围 作用类似于Pytest的setup/teardown 默认取值为function(函数级别),控制范围的排序为:session > module > class > function
|
作用范围举例: scope = “function” 语法: @pytest.fixture() #或者 @pytest.fixture(scope='function')
场景一:做为参数传入
import pytest # fixture函数(类中) 作为多个参数传入 @pytest.fixture() def login(): print("打开浏览器") a = "account" return a @pytest.fixture() def logout(): print("关闭浏览器") class TestLogin: #传入lonin fixture def test_001(self, login): print("001传入了loging fixture") assert login == "account" #传入logout fixture def test_002(self, logout): print("002传入了logout fixture") def test_003(self, login, logout): print("003传入了两个fixture") def test_004(self): print("004未传入仍何fixture哦") if __name__ == '__main__': pytest.main()
运行pytest命令结果如下:
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 4 items test_fixture1.py [100%] ============================== 4 passed in 0.03s ==============================11打开浏览器 .001传入了loging fixture 关闭浏览器 .002传入了logout fixture 11打开浏览器 关闭浏览器 .003传入了两个fixture .004未传入仍何fixture哦 Process finished with exit code 0
场景二、Fixture的相互调用
import pytest # fixtrue作为参数,互相调用传入 @pytest.fixture() def account(): a = "account" print("第一层fixture") return a #Fixture的相互调用一定是要在测试类里调用这层fixture才会生次,普通函数单独调用是不生效的 @pytest.fixture() def login(account): print("第二层fixture") class TestLogin: def test_1(self, login): print("直接使用第二层fixture,返回值为{}".format(login)) def test_2(self, account): print("只调用account fixture,返回值为{}".format(account)) if __name__ == '__main__': pytest.main()
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 2 items test_fixture1.py [100%] ============================== 2 passed in 0.03s ==============================第一层fixture 第二层fixture .直接使用第二层fixture,返回值为None 第一层fixture .只调用account fixture,返回值为account Process finished with exit code 0
2.scope = “class”:【@pytest.fixture(scope='class')】
*当测试类内的每一个测试方法都调用了fixture,fixture只在该class下所有测试用例执行前执行一次
**测试类下面只有一些测试方法使用了fixture函数名,这样的话,fixture只在该class下第一个使用fixture函数的测试用例位置开始算,后面所有的测试用例执行前只执行一次。而该位置之前的测试用例就不管。
语法
场景一、
import pytest # fixture作用域 scope = 'class' @pytest.fixture(scope='class') def login(): print("scope为class") class TestLogin: def test_1(self, login): print("用例1") def test_2(self, login): print("用例2") def test_3(self, login): print("用例3") if __name__ == '__main__': pytest.main()
结果
============================== 3 passed in 0.03s ==============================scope为class .用例1 .用例2 .用例3 Process finished with exit code 0
场景二、
import pytest @pytest.fixture(scope='class') def login(): a = '123' print("输入账号密码登陆") class TestLogin: def test_1(self): print("用例1") def test_2(self): print("用例2") def test_3(self, login): print("用例3") def test_4(self): print("用例4") if __name__ == '__main__': pytest.main()
结果
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 4 items test_fixture1.py .用例1 .用例2 输入账号密码登陆 .用例3 .用例4 [100%] ============================== 4 passed in 0.03s ============================== Process finished with exit code 0
scope = “module”:与class相同,只从.py文件开始引用fixture的位置生效
scope = “session”:用法将在conftest.py文章内详细介绍
session的作用范围是针对.py级别的,module是对当前.py生效,seesion是对多个.py文件生效
session只作用于一个.py文件时,作用相当于module
所以session多数与contest.py文件一起使用,做为全局Fixture
2、params:
Fixture的可选形参列表,支持列表传入 默认None,每个param的值 fixture都会去调用执行一次,类似for循环 可与参数ids一起使用,作为每个参数的标识,详见ids 被Fixture装饰的函数要调用是采用:Request.param(固定写法,如下图)
import pytest @pytest.fixture(params=[1, 2, {'a': 1, 'b': 2}, {'A': 1, 'B': 2}]) def demo(request): return request.param def test_demo(demo): print("列表值:{}".format(demo)) if __name__ == '__main__': pytest.main()
结果
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 4 items test_fixture1.py [100%] ============================== 4 passed in 0.03s ==============================.列表值:1 .列表值:2 .列表值:{'a': 1, 'b': 2} .列表值:{'A': 1, 'B': 2} Process finished with exit code 0
''' request 是 pytest的内置fixture ''' import pytest # 测试数据 test_data = ["user1", "user2"] @pytest.fixture(params=test_data) def register_users(request): # 获取当前的测试数据 user = request.param print("setup前置函数拿着这个账号去注册:%s" % user) result = "success" return user, result def test_register(register_users): user, result = register_users print("在测试用例里面里面获取到当前测试数据:%s" % user) print(result) assert result == "success" @pytest.fixture(autouse=True) def show_request(request): print("\n=======================request start=================================") print("request.module==", request.module) print("request.functione==", request.function) print("request.cls==", request.cls) print("request.fspath==", request.fspath) print("request.fixturenames==", request.fixturenames) print("request.fixturename==", request.fixturename) print("request.scope==", request.scope) print("\n=======================request end=================================") if __name__ == '__main__': print(11)
3、ids:
用例标识ID
与params配合使用,一对一关系
举个栗子:
未配置ids之前,用例:
import pytest @pytest.fixture(params=[1, 2, {'a': 1, 'b': 2}, {'A': 1, 'B': 2}],ids=["one","two","three","four"]) def demo(request): return request.param def test_demo(demo): print("列表值:{}".format(demo)) if __name__ == '__main__': pytest.main()
#使用前后的区别
5、Name:
fixture的重命名 通常来说使用 fixture 的测试函数会将 fixture 的函数名作为参数传递,但是 pytest 也允许将fixture重命名 如果使用了name,那只能将name传如,函数名不再生效 调用方法:@pytest.mark.usefixtures(‘fixture1’,‘fixture2’)
结果
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0collected 2 items test_fixture1.py .使用name参数后,传入重命名函数,执行成功 E test setup failed file E:\FYR\python\111\test_fixture1.py, line 17 def test_2(test_name): E fixture 'test_name' not found
2.2 fixture作为参数传入
定义fixture跟定义普通函数差不多,唯一区别就是在函数上加个装饰器@pytest.fixture(),fixture命名不要用test_开头,跟用例区分开。用例才是test_开头的命名。
fixture是可以有返回值的,如果没return默认返回None。用例调用fixture的返回值,直接就是吧fixture的函数名称当成变量名称,
import pytest @pytest.fixture() def user(): print("获取用户名") a = "admin" return a def test_1(user): print("a==", user) assert user == "admin" if __name__ == "__main__": pytest.main(["-s", "test_fixture1.py"])
结果如下:
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0 collected 1 item test_fixture1.py 获取用户名 a== admin . ============================== 1 passed in 0.16s ============================== Process finished with exit code 0
2.3error和failed区别
测试结果一般有三种:passed、failed、error。(skip的用例除外)
如果在test_用例里面断言失败,那就是failed
import pytest @pytest.fixture() def user(): print("获取用户名") a = "admin" return a def test_1(user): assert user == "admin111" if __name__ == "__main__": pytest.main(["-s", "test_fixture1.py"])
结果如下:
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0 collected 1 item test_fixture1.py 获取用户名 F ================================== FAILURES =================================== ___________________________________ test_1 ____________________________________ user = 'admin' def test_1(user): > assert user == "admin111" E AssertionError: assert 'admin' == 'admin111' E - admin111 E ? --- E + admin test_fixture1.py:11: AssertionError =========================== short test summary info =========================== FAILED test_fixture1.py::test_1 - AssertionError: assert 'admin' == 'admin111' ============================== 1 failed in 0.15s ============================== Process finished with exit code 0
如果在fixture里面断言失败了,那就是error
import pytest @pytest.fixture() def user(): print("获取用户名") a = "admin" assert a == "admin123" return a def test_1(user): assert user=="admin" if __name__ == "__main__": pytest.main(["-s", "test_fixture1.py"])
结果如下:
============================= test session starts ============================= platform win32 -- Python 3.7.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 rootdir: E:\FYR\python\111 plugins: html-3.1.1, metadata-1.11.0 collected 1 item test_fixture1.py 获取用户名 E =================================== ERRORS ==================================== __________________________ ERROR at setup of test_1 ___________________________ @pytest.fixture() def user(): print("获取用户名") a = "admin" > assert a == "admin123" E AssertionError: assert 'admin' == 'admin123' E - admin123 E ? --- E + admin test_fixture1.py:8: AssertionError =========================== short test summary info =========================== ERROR test_fixture1.py::test_1 - AssertionError: assert 'admin' == 'admin123' ============================== 1 error in 0.16s =============================== Process finished with exit code 0
四、request.config.getoption介绍
import pytest def pytest_addoption(parser): parser.addoption("--name", action="store", default="zhangsan", help="my option: name") parser.addoption("--tel", action="store", default="18266669999", help="my option: tel") @pytest.fixture(scope='function') # 根据类型,显示作用范围 def start_settings(request): # 获取--name name = request.config.getoption("--name") # 返回自定义变量的值 tel = request.config.getoption("--tel") # 返回自定义变量的值 yield name, tel # 返回给测试用例使用 def test_case1(start_settings): name, tel = start_settings print("name===", name) print("tel===", tel)