pytest----fixture
前言
- setup、teardown可以实现在执行用例前或结束后加入一些操作,但这种都是针对整个脚本全局生效的
- 如果有以下场景:用例 1 需要先登录,用例 2 不需要登录,用例 3 需要先登录。很显然无法用 setup 和 teardown 来实现了
- fixture可以让我们自定义测试用例的前置条件
fixture的优势
- 命名方式灵活,不局限于 setup 和teardown 这几个命名
- conftest.py 配置里可以实现数据共享,不需要 import 就能自动找到fixture
- scope="module" 可以实现多个.py 跨文件共享前置
- scope="session" 以实现多个.py 跨文件使用一个 session 来完成多个用例
首先定义fixture
,怎么定义呢?
fixture: 即测试用例执行的环境准备和清理,在unitest中即指setup/teardown/setupClass/teardownClass
fixture主要目的是为了提供一种可靠和可重复性的手段去运行那些最基本的测试内容。比如在测试网址的功能时,每个测试用例都要登录和退出,利用fixture就可以只做一次,否则每个测试用例都要做这两步。
定义fixture
把一个函数定义为fixture很简单,在函数声明之前加上@pytest.fixture,
表示此函数作为测试环境数据的准备和清理。
那么在一个fixture内部如何区分环境准备和环境清理呢?
在函数内使用yield关键字。
yield关键字之后的代码,就是环境清理的代码,即在测试用例执行完之后会执行的代码
fixture返回值:
fixture定义之后的使用
在pytest
中,环境准备和环境清理是完全放在一起的。在unittest
中,它是两个都分开的,一个是setUp
,一个是tearDown
,我们会定义两个函数。
在pytest
中,只要一个函数就可以了。fixture
可以在当前的文件中来定义,也可以额外得去定义。
第一种,公有化的方式:
前置和后置定义在特殊的文件当中,以后谁想要用,就直接调用就好了。一般调用函数,需要引进来才能调用,但是在pytest
当中不需要。直接用个装饰器引用下就可以了,完全不需要引用这个文件。
1.它是怎么实现这种公有化的方式呢?
在TeatCase
目录下,新建一个Python
文件,文件名固定是:conftest
。
这个文件就是个公有化的文件。
conftest
文件必须和测试用例放在一起,和测试用例文件是同级。
fixture
是一个函数。
可以看到源码解析:
def fixture( callable_or_scope=None, *args, scope="function", params=None, autouse=False, ids=None, name=None ): """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)`
可以看到第一个参数是scope
,scope
就是会话级、模块级、类级、函数级。代表它的作用域,默认是function
。什么是function
?
函数是指单个的测试用例,也就是每一个测试用例。剩下的一些参数可以暂时不用管。
默认的是function
,代表的是setUp
和tearDown
。测试用例是以函数的形式呈现的。既然是函数级别的,也就是一个测试用例一个测试用例的。类级别的参数,源码解析:
:arg scope: the scope for which this fixture is shared, one of ``"function"`` (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"`` (``"package"`` is considered **experimental** at this time).
参数有function
,class
。class
就是setUpClass
和tearDownClass
。
setUp
和tearDown
是作用在每个测试用例上,setUpClass
和tearDownClass
是作用在测试类当中,module
就是整个Py文件。package
是包,session
是会话。这个会话是指测试用例会话。高级别的用法暂时用不上,就不用管它。
session
级别的,在接口自动化中可能用得上。每个测试用例可能都涉及数据库校验,它的前提就是数据库连接,得到一个数据库对象以及游标操作。那么就可以用session
了。
在所有测试用例之前,先把这个东西运行一下,在后续所有操作中,直接用这个对象就行了。不需要在不同的测试用例中去连接。
实际上,接口自动化中用setUpClass
就可以解决这个问题。
2.怎么知道在这个access_web
函数中,哪些代码是前置,哪些代码是后置啊?
用关键字yield
分隔前置操作和后置操作。如果没用后置,那么关键字yield
都不用写
import pytest from selenium import webdriver from PageObjects.login_page import LoginPage from TestDatas import Comm_Datas as cd driver=None #声明它是一个fixture @pytest.fixture(scope="class") def access_web():#说明整个测试类只运行一次 global driver #前置操作 print("=======所有测试用例执行之前的,setup====整个测试类只执行一次======") driver=webdriver.Chrome() driver.get(cd.web_login_url) lg=LoginPage(driver) yield(driver,lg) #分隔线;#后面接返回值 print("========所有测试用例之后的,teardown====整个测试用例只执行一次======") driver.quit() #后置操作 @pytest.fixture#默认是`setUp`和`tearDown`,所以这里不需要带括号。 def refresh_page(): global driver #前置操作 yield #后置操作 driver.refresh() @pytest.fixture(scope="session") def session_demo(): print("****我是整个测试会话期间的开始****") yield print("****我是整个测试会话期间的结束****") @pytest.fixture(scope="class") def class_demo(): print("****我是class的开始****") yield print("****我是class的结束****") @pytest.fixture def func_demo(): print("****我是function的开始****") yield print("****我是function的结束****")
@pytest.mark.usefixtures("refresh_page")
可以写在测试用例前面,但是这个测试类下面每一个测试用例都用到了 fixture
,大家的操作都是一样的,就没必要在每个函数前面都写上它。直接把它放在类前面就可以了。表示大家都用。
@pytest.mark.usefixtures("access_web")#在运行的时候,会去运行access_web函数 @pytest.mark.usefixtures("refresh_page") class TestLogin: # 正常用例 - 登陆成功 #fixture的函数名称,用来接收它的返回值 @pytest.mark.smoke def test_login_2_success(self,access_web):#fixture的函数名称作为用例参数,用来接收fixture的返回值 logging.info("****登陆用例:正常场景:适用正确的用户名和密码登陆***") #步骤 输入用户名和密码 点击登陆 access_web[1].login(ld.success_data["user"],ld.success_data["passwd"]) #断言 首页当中,如何找到退出这个元素 assert IndexPage(self.access_web[0]).isExist_logout_ele()
登陆的话,可以不需要它,用setUp
和tearDown
就可以解决这个问题。因为它的前置和后置只有它自己用,没用别人用。
二种,私有化的方式:
假如TestLogin中9个前置后置都一样,只有1个前置后置不一样,该怎么做?
此代码运行可能会报错,用法需根据实际代码调试:
@pytest.mark.usefixtures("func_demo")#在运行的时候,会去运行access_web函数 @pytest.mark.usefixtures("class_demo") @pytest.mark.demo def test_demo(): print("ffffffffffffffff")
1.把单独的这一个拎取出来,定义成一个函数就好了,作为函数的形式放在外面。其它9个一样的都放在类当中。
2.假如10个测试用例中,5个是这样的,另外5个是另外一个样子的。就可以定义2个测试类。2个测试类用不一样的fixture
。
以上私有化的方式的2种方法需要实操,根据实际情况判断2种方法的可用性以及调试。
fixture参数列表
@pytest.fixture(scope="function", params=None, autouse=False, ids=None, name=None) def test(): print("fixture初始化的参数列表")
参数列表
- scope:可以理解成fixture的作用域,默认:function,还有class、module、package、session四个【常用】
- autouse:默认:False,需要用例手动调用该fixture;如果是True,所有作用域内的测试用例都会自动调用该fixture
- name:默认:装饰器的名称,同一模块的fixture相互调用建议写个不同的name
注意
session的作用域:是整个测试会话,即开始执行pytest到结束测试
测试用例如何调用fixture
- 将fixture名称作为测试用例函数的输入参数
- 测试用例加上装饰器:@pytest.mark.usefixtures(fixture_name)
- fixture设置autouse=True
# -*- coding: utf-8 -*- """ __title__ = pytest study __Time__ = 2021-04-12 08:47 __Author__ = sary """ #!/usr/bin/env python # -*- coding: utf-8 -*- """ __title__ = __Time__ = 2020-04-06 15:50 __Author__ = 小菠萝测试笔记 __Blog__ = https://www.cnblogs.com/poloyy/ """ import pytest # 调用方式一 @pytest.fixture def login(): print("输入账号,密码先登录") def test_s1(login): print("用例 1:登录之后其它动作 111") def test_s2(): # 不传 login print("用例 2:不需要登录,操作 222") # 调用方式二 @pytest.fixture def login2(): print("please输入账号,密码先登录") @pytest.mark.usefixtures("login2", "login") def test_s11(): print("用例 11:登录之后其它动作 111") # 调用方式三 @pytest.fixture(autouse=True) def login3(): print("====auto===") # 不是test开头,加了装饰器也不会执行fixture @pytest.mark.usefixtures("login2") def loginss(): print(123)