Python中Pytest测试框架的fixture使用详解
作者:橙子软件测试菇凉
1、fixture的含义
fixture的目的是提供一个测试的基线,在此基线基础上,可以更可靠的进行重复测试。
2、fixture的优势
Pytest的fixture相对于传统的xUnit的setup/teardown函数做了显著的改进:
(1)测试fixture有明确的名称,通过在函数/模块/类或者整个项目中激活来使用
(2)测试fixture是模块化的实现,使用fixture名即可触发特定的fixture,fixture可以在其他fixture中进行使用测试fixture不仅可以进行简单的单元测试,也可以进行复杂的功能测试。可以根据配置和组件的选项进行参数化定制测试,或者跨函数/类/模块或者整个测试过程进行测试。
(3)pytest依然支持经典的xUnit的样式,你可以根据自己的喜好混合两种样式。
3、fixture参数
- scope
- params
- autouse
- ids
- name
def fixture( fixture_function: Optional[_FixtureFunction] = None, *, scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function", params: Optional[Iterable[object]] = None, autouse: bool = False, ids: Optional[ Union[ Iterable[Union[None, str, float, int, bool]], Callable[[Any], Optional[object]], ] ] = None, name: Optional[str] = None, ) -> Union[FixtureFunctionMarker, _FixtureFunction]: """Decorator to mark a fixture factory function. This decorator can be used, with or without parameters, to define a fixture function. The name of the fixture function can later be referenced to cause its invocation ahead of running tests: test modules or classes can use the ``pytest.mark.usefixtures(fixturename)`` marker. Test functions can directly use fixture names as input arguments in which case the fixture instance returned from the fixture function will be injected. Fixtures can provide their values to test functions using ``return`` or ``yield`` statements. When using ``yield`` the code block after the ``yield`` statement is executed as teardown code regardless of the test outcome, and must yield exactly once. :param scope: The scope for which this fixture is shared; one of ``"function"`` (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``. This parameter may also be a callable which receives ``(fixture_name, config)`` as parameters, and must return a ``str`` with one of the values mentioned above. See :ref:`dynamic scope` in the docs for more information. :param params: An optional list of parameters which will cause multiple invocations of the fixture function and all of the tests using it. The current parameter is available in ``request.param``. :param autouse: If True, the fixture func is activated for all tests that can see it. If False (the default), an explicit reference is needed to activate the fixture. :param ids: List of string ids each corresponding to the params so that they are part of the test id. If no ids are provided they will be generated automatically from the params. :param name: The name of the fixture. This defaults to the name of the decorated function. If a fixture is used in the same module in which it is defined, the function name of the fixture will be shadowed by the function arg that requests the fixture; one way to resolve this is to name the decorated function ``fixture_<fixturename>`` and then use ``@pytest.fixture(name='<fixturename>')``. """ fixture_marker = FixtureFunctionMarker( scope=scope, params=params, autouse=autouse, ids=ids, name=name, ) # Direct decoration. if fixture_function: return fixture_marker(fixture_function) return fixture_marker
4、实战一:fixture作为参数使用
测试函数可以通过接受一个已经命名的fixture对象来使用它们。对于每个参数名,如果fixture已经声明定义,会自动创建一个实例并传入该测试函数。
fixture函数通过装饰器标志@pytest.fixture来注册。
下面是一个简单的独立的测试模块,包含一个fixture及使用它的测试函数。
使用场景:
- 用例1需要登录
- 用例2不需要登录
- 用例3需要登录
用法
在方法前面加@pytest.fixtrue()
实战
- 单个fixture
import pytest @pytest.fixture() def login(): print("这个是登录方法") def test_case1(login): print("test_case1,需要登录") pass def test_case2(): print("test_case2,不用登录") pass def test_case3(login): print("test_case3,需要登录") pass if __name__ == '__main__': pytest.main()
- 多个fixture
import pytest //名字可以作为参数 @pytest.fixture() def login(): print("这个是登录方法") return ('tom',"123") @pytest.fixture() def operate(): print("登录后的操作") def test_case1(login,operate): print(login) def test_case2(): print("test_case2,不用登录") def test_case3(login): print(login) if __name__ == '__main__': pytest.main()
5、实战二:fixture的作用范围
测试函数可以通过接受一个已经命名的fixture对象来使用他们。
对于每个参数名,如果fixture已声明定义,会自动创建一个实例并传入该测试函数。fixture函数通过装饰器标志@pytest.fixture来注册。
下面是一个简单的独立的测试模块,包含一个fixture及使用它的测试函数
名称 | 范围 | 说明 |
function | 函数级 | 每一个函数或方法都会调用 |
class | 类级别 | 每个测试类只运行一次 |
module | 模块级 | 每一个.py文件调用一次 |
package | 包级 | 每一个python包只调用一次(暂不支持) |
session | 会话级 | 每次会话只需要运行一次,会话内所有方法及类,模块都共享这个方法 |
作用范围顺序:session》module》class》function
使用场景
整个模块有多条测试用例,需要在全部用例执行之前打开浏览器,全部执行之后去关闭浏览器,打开和关闭操作只执行一次。
import pytest # 作用域:module是在模块之前执行,模块之后执行 @pytest.fixture(scope="module") def open(): print("打开浏览器") yield print("执行teardown!") print("最后关闭浏览器") @pytest.mark.usefixtures("open") def test_search1(): print("test_search1") def test_search2(open): print("test_search2") def test_search3(open): print("test_search3")
return 与 yield的区别:
- return:在程序函数中返回某个值,返回之后函数不在继续执行,彻底结束。
- yield: 带有yield的函数是一个迭代器,函数返回某个值时,会停留在某个位置,返回函数值后,会在前面停留的位置继续执行直到程序结束
6、实战三:fixture与conftest.py结合使用
在使用之前我们先来了解什么是conftest.py?
实现测试用例的过程中,当你发现需要使用来自多个文件的fixture函数的时候,可以将这些fixture函数放到conftest.py中。
你不需要导入这些fixture函数,它会由pytest自动检索。
fixture函数的检索顺序是从测试类开始,然后测试的模块,然后就是conftest.py文件,最后是内置的插件和第三方插件。
使用场景
你与其他测试工程师合作一起开发时,公共的模块要在不同文件中,要在大家都访问到的地方(建议可以放在项目的根目录,因为找到conftest.py文件,先在同级目录找,如果同级目录找不到时,会向上级目录继续找,指导找到为止)
ps:conftest.py名称是固定,不能修改
实战
conftest.py的内容如下:
import pytest #这个一定不要忘记写 @pytest.fixture(scope="session") def login(): # setup操作 print("用户需要先完成登录操作") username = "hxc" name = "pytest书籍" # 相当于return,但是return不会执行后面的语句,yield是可以 yield username,name # teardown操作 print("完成登出操作")
test_conftestDemo.py的内容如下:
import pytest def test_search(login): username,name = login print(f"用户名:{username},搜索商品:{name}") def test_cart(login): print("添加购物车") def test_order(): print("下单pytest书籍") def test_login(login): print("添加购物车之前先登录") def test_get_product(connectDB): print("获取商品信息先连接数据库") @pytest.fixture() def connectDB(): print("连接数据库") yield print("断开数据库")
7、实战四:fixture参数化
测试过程中需要大量的测试数据,如果每一个测试用例都编写一条测试用例,用例将是非常庞大的。
一般会使用将测试用例用到的数据一参数的形式传入待测试用例中,并为每条测试数据生成一个测试结果数据。
使用场景
测试离不开数据,为了数据灵活,一般数据都是通过参数传的
使用方法
在fixture方法上加装饰器@pytest.fixture(params=[1,2,3]),就会传入三个数据1,2,3,分别将这三个数据传入到用例当中。传入的数据需要使用一个固定的参数名request来接收。
import pytest @pytest.fixture(params=["selenium", "appium"]) def login(request): print(f"用户名:{request.param}") return request.param def test_demo1(login): print(f"demo1 case 数据为: {login}")
import pytest @pytest.fixture(params=[["selenium",123],["appium",123456]]) def login(request): print(f"用户名:{request.param}") return request.param def test_demo1(login): print(f"demo1 case: 数据为: {login}")
到此这篇关于Python中Pytest测试框架的fixture使用详解的文章就介绍到这了,更多相关Pytest框架的fixture内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!