Pytest2、前后置处理高级函数Fixture

大佬的文章:https://www.cnblogs.com/liudinglong/p/12885632.html#comment_form

fixture的目的是提供一个固定基线,在该基线上测试可以可靠地和重复地执行。fixture提供了区别于传统单元测试(setup/teardown)有显著改进:

  1.有独立的命名,并通过声明它们从测试函数、模块、类或整个项目中的使用来激活。

  2.按模块化的方式实现,每个fixture都可以互相调用。

  3.fixture的范围从简单的单元扩展到复杂的功能测试,允许根据配置和组件选项对fixture和测试用例进行参数化,或者跨函数function、类class、模块module或整个测试会话sessio范围。

def fixture(
    callable_or_scope=None,
    *args,
    scope="function",		# scope作用域有4个级别,默认是function,其次class,然后是module和session.
    params=None,		# 一个可选的形参列表,它将导致多个参数对夹具功能和所有测试的调用使用它。
    autouse=False,		# 如果为真,则对所有测试激活fixture func可以看到它。如果为False(默认值),则显式需要引用来激活夹具。
    ids=None,			# 每个参数对应的字符串id列表因此它们是测试id的一部分。如果没有提供id它们将由参数自动生成。
    name=None			# 设备的名称。方法的默认名称装饰功能。如果在同一模块中使用了一个fixture哪个定义了,夹具的函数名会是被要求夹具的功能参数所遮蔽;的一种方法要解决这个问题,可以命名修饰后的函数'fixture_<fixturename>'然后使用@pytest.fixture (name = ' < fixturename > ')。
):


完整调试代码,以后有空再整理

test_pytest_Fixture.py

# -*- coding: utf-8 -*-
#test_pytest_Fixture.py

import pytest

""" 
调用fixture四种方法

    1.函数或类里面方法直接传fixture的函数参数名称

    2.使用装饰器@pytest.mark.usefixtures()修饰

    3.使用pytestmark = pytest.mark.usefixtures('login')

    4.autouse=True自动使用

 """








# @pytest.fixture()函数使用方式:作为参数传入(单个)
#---------------------------------创建fixture函数(无类)——法1,作为参数传入,作为范围:functions---------------------------------------------------
""" 

@pytest.fixture()
def login():
    print("\n输入用户名密码登陆! test_pytest_Fixture.py")
    a = "account"
    return a
 
 
def test_001(login):
    print("账号是: %s"%login)
    assert login == "account"
 
def test_002():
    print("单击登陆")

"""

""" 
# 创建fixture函数(类中)——法2,作为参数传入,作为范围:functions
@pytest.fixture(scope="class")
def login():
    print("\n输入用户名密码登陆! test_pytest_Fixture.py")
    a = "account"
    return a
 
 
class TestLogin:
    def test_001(self,login):
        print("输入的账号: %s"%login)
        assert login == "account"


    def test_002(self):
        print("单击登陆")

"""

# -----------------------------------@pytest.fixture()函数使用方式:作为参数传入(多个fixture使用)-------------------------------------------------
# 一些场景,比如登陆之后有退出,这样的话需要两个fixture函数处理,示例如下

""" 
# fixture函数(类中) 作为多个参数传入
@pytest.fixture()
def login():
    print("\n输入用户名密码登陆! test_pytest_Fixture.py")
    a = "account"
    return a
 
@pytest.fixture()
def logout():
    print("退出中...")
 
class TestLogin:
    def test_001(self,login):
        print("输入的账号: %s"%login)
        assert login == "account"   # 账号密码正确,登录成功,继续后续的步骤

    def test_002(self,logout):
        print("已执行退出")

    def test_003(self,login,logout):
        print("步骤1:%s"%login)     #先执行fixture 后执行打印
        print("步骤2:%s"%logout)    #没有返回值,打印为None

"""


# -----------------------------------@pytest.fixture()函数使用方式:作为参数传入(互相调用)-------------------------------------------------
""" 
# fixture固件可以被测试方法调用,也可以被固件自己调用。
@pytest.fixture()
def account():
    a = "account"
    print("\n输入用户名密码登陆! test_pytest_Fixture.py")
 
@pytest.fixture()
def login(account):
    print("单击登陆")
 
class TestLogin:
    def test_1(self,login): #参数传入调用login,login调用account
        print("操作结果:%s"%login)  #没有再次调用,只读取返回

    def test_2(self,account):       # 仅调用account(),没调用login
        print("账号: %s"%account)   #没有返回值,取值为None

    def test_3(self):               # 都没调用,独立的一个case
        print("测试用例3")

 """


# -----------------------------------@Fixture作用范围:scope = 'class'-------------------------------------------------

""" 
# 第一种,测试类下面所有测试方法(用例),都使用了fixture函数名,这样的话,fixture只在该class下所有测试用例执行前执行一次。(那和后面的用例都不加 有啥区别= =)
@pytest.fixture(scope='class')
def login():
    a = '123'
    print("\n输入用户名密码登陆! test_pytest_Fixture.py")
    return '\t成功登录'

"""
 
""" 
class TestLogin:
    def test_1(self,login):
        print("用例1")

    def test_2(self,login):
        print("用例2")

    def test_3(self,login):
        print("用例3")

 """

"""  
# 第二种,测试类下面只有一些测试方法使用了fixture函数名,这样的话,fixture只在该class下第一个使用fixture函数的测试用例位置开始算,后面所有的测试用例执行前只执行一次。而该位置之前的测试用例就不管。
class TestLogin2:
    def test_1(self):
        print("用例1%s"%login)  # 未以参数代入,无法取值                    》》》          用例1<function login at 0x00000233A5CFC680>

    def test_2(self,login):     
        print("用例2%s"%login)  # 仅第一次使用fixture的地方 调用login方法    》》》           输入账号密码登陆  用例2   成功登录

    def test_3(self,login):     
        print("用例3%s"%login)  # 只调用1次,这次就不调用了                  》》》           用例3 成功登录

    def test_4(self):
        print("用例4%s"%login)  # 未以参数代入,无法取值                     》》》          用例1<function login at 0x00000233A5CFC680>


 """

# -----------------------------------@Fixture作用范围:scope = 'module'-------------------------------------------------

# fixture为module时,对当前模块(.py)文件下所有测试用例开始前执行一次,示例如下:
""" 
@pytest.fixture(scope='module')

def login():
    print("输入账号密码登陆")
    return "\t登录成功"
 
def test_01(login):     #仅第一个用例执行时 调用Login执行一次,之后可以使用返回值(token之类)
    print("用例01%s"%login)

def test_02(login):     #不执行逻辑,可以直接取返回值
    print("用例02%s"%login)
 
class TestLogin():
    def test_1(self,login):
        print("用例%s"%login)

    def test_2(self,login):
        print("用例%s"%login)

    def test_3(self,login):
        print("用例%s"%login)

 """




# -----------------------------------@Fixture作用范围:scope = 'session'    单个模块内使用-------------------------------------------------
# 设置方式和module级别的设置方式一样,需要注意的是session级别一般都是多个.py文件共用,所以要前置函数的声明一般在conftest.py中。
# 其作用在多个测试模块(.py文件)中只执行一次,并且是在传入函数名的测试用例中的第一个执行的测试用例之前执行。
""" 
@pytest.fixture(scope="session")
def login():
    print("\n输入用户名密码登陆! test_pytest_Fixture.py")

    yield   # 后置方法
    print("退出登陆,清理数据完成")       #最后执行后置

    return '已退出登录'                 #返回的结果取不到


def test_cart(login):
    print('用例1,登陆后添加购物车功能操作,获取结果%s'%login)  #只会首次参数化Login的时候调用执行,因为有后置所以获取不到返回    》》》      用例1,登陆后添加购物车功能操作,获取结果None
 
def test_search():
    print('用例2,不登陆查询功能操作,获取结果%s'%login)    #未调用Login,也取不到结果      》》》     用例2,不登陆查询功能操作,获取结果<function login at 0x0000017E8F708680> 
 
def test_pay(login):
    print('用例3,登陆后执行支付功能操作,获取结果%s'%login)    #最后用到login参数的用例,因为后置所以取不到结果,最后执行后置      》》》     用例3,登陆后执行支付功能操作,获取结果None    》》》      退出登陆

 """



# -----------------------------------@Fixture作用范围:scope = 'session'    多文件使用------------------------------------------------
# 如果在不同模块下(.py文件里),session是给多个.py文件使用,并且写到conftest.py文件里,conftest.py文件名称是固定的,pytest会自动识别该文件。
# 放到工程的根目录下,就可以全局调用了,如果放到某个package包下,那就只在该package内有效,如同上面的例子

# 在该文件目录下新建conftest.py文件:
""" 
# 本文件调用
class TestLogin1():
    def test_1(self,login):     # 跨文件调用conftest.py 内的Fixture,无需引用      》》》     输入账号密码conftest.py[login]      》》》     用例1PASSED
        print("用例1")
    def test_2(self):
        print("用例2")          # 没有调用的用例方法                  》》》     用例2PASSED
    def test_3(self,login):
        print("用例3")          # 仅单文件执行的话,用例3 执行结束就会执行login的后置方法      》》》     用例3PASSED      》》》     已退出登录,清理数据完成

 """





""" 
# 这个时候如果你想要模块中的每个测试用例都调用该固件前置后置,你也可以使用pytestmark标记:(注意pytestmark变量名不可更改),示例如下:      》》》     没每个方法都调用1次前后置,没达到效果。。。
pytestmark = pytest.mark.usefixtures('login')
class TestLogin5():

    def test_13(self):
        print("用例13")

    def test_14(self):
        print("用例14")

    def test_15(self):
        print("用例15")

 """


# ------------------------------------------------Fixture调用方式4:autouse="true"    自动调用------------------------------------------------

# 可以使用@pytest.fixture()中的参数autouse(自动使用),将其设为true(默认为false),这样每个测试函数都会自动调用该前置函数了。      》》》     实现上面失败的每个test_方法都执行前后置
# 如果可以的话,尽量应当选择参数传递或者 usefixtures 的方法而不是 autouse。autouse 会让测试函数逻辑看上去没有那么清晰,更像是一个特例。
# conftest.py 中的优先级更高
# -------------------------------------------------------------------------------------------------------------------------------------------
""" 
# 4.autouse=True自动使用,有配置的,所有test_开头的,都会使用
@pytest.fixture(autouse="true")
def login():
    print("\n输入用户名密码登陆! test_pytest_Fixture.py")

    yield   # 后置方法
    print("已退出登录,清理数据完成 test_pytest_Fixture.py")       #最后执行后置

    return '已退出登录'                 # 后置里返回的结果,返回前都取不到
 

class TestLogin6:
    def test_16(self):                  # 因为自动调用,所以即使不把Fixture传参或调用,也会调用执行
        print("用例16")
    def test_17(self):
        print("用例17")


 """


# ------------------------------------------------Fixture带参方法:params=tasks_list  参数param------------------------------------------------
# fixture可以带参数param,params支持列表;
# param里面的每个值,fixture都会去调用执行一次,就像执行for循环一样把params里的值遍历一次,默认是None;
""" 
tasks_list=[1,2,3]
@pytest.fixture(params=tasks_list)
def test_data(request): #在 pytest 中有一个内建的 fixture 叫做 request,代表 fixture 的调用状态。request 有一个字段 param,可以使用类似@pytest.fixture(param=tasks_list)的方式,在 fixture 中使用 request.param的方式作为返回值供测试函数调用。其中 tasks_list 包含多少元素,该 fixture 就会被调用几次,分别作用在每个用到的测试函数上。
    print("\n输入用户名密码登陆!,当前输入%s"%request.param)
    return request.param

class TestData:
    def test_18(self,test_data): # 调用test_data fixture ,
        print("用例18,参数:",test_data)

    def test_19(self,test_data): # 调用test_data fixture ,
        print("用例19,参数:",test_data)

 """


# ------------------------------------------------ Fixture用例执行标记方法:ids= task_ids  标记 ids ------------------------------------------------

# ids通常可以与params一起使用,由于没有指定 id,所以在输出时 pytest 会以 fixture 名加上数字作为标识,fixture 也可以指定 id,
# 例如@pytest.fixture(param=tasks_list,ids=task_ids)  ids可以是列表,也可以是函数供 pytest 生成 task 标识。
""" 
task_ids = ["a","b","c"]
@pytest.fixture(params=[1,2,3],ids=["CaseA Params = 1","CaseB Params = 2","CaseC Params = 3"])
def test_data2(request):
    print("\n输入用户名密码登陆!,当前输入%s"%request.param)   
    # print(request)
    return request.param
 
 
class TestData:
    def test_20(self,test_data2):       # 用例的结果加上了标识:第一个用例,参数X等,方便识别,不影响执行的   test_pytest_Fixture.py::TestData::test_20[CaseB Params = 2]
        print("用例20",test_data2)      # 每个都会执行3次,因为params 传入3个。

    def test_21(self,test_data2):       # test_pytest_Fixture.py::TestData::test_21[CaseC Params = 3]
        print("用例21",test_data2)      # 每个都会执行3次,因为params 传入3个。

 """



# ------------------------------------------------ Fixture 用例执行标记方法:ids= task_ids   Name参数 ------------------------------------------------

# 通常来说使用 fixture 的测试函数会将 fixture 的函数名作为参数传递,
# 但是 pytest 也允许将 fixture 重命名。只需要使用@pytest.fixture(name="new")即可,在测试函数中使用该 fixture 时只需要传入 new 即可。



@pytest.fixture(name="new_fixture")
def test_name():
    print("执行fixture") 
    return "执行成功"
 
def test_22(new_fixture):
    print("测试用例22,修改fixture的默认函数名:test_name  为 new_fixture \t %s"%new_fixture)



# pytest.main()  默认运行当前文件所在目录下所有符合条件的case
# pytest.main(["文件名/目录地址"])  运行指定文件/或目录下的所有case
# pytest main(["文件名::类名"])   运行指定类
# pytest main(["文件名::类名::方法名"])  运行指定方法
# pytest main(["文件名::函数名"]) 运行指定函数
if __name__ == '__main__':
    pytest.main(['test_pytest_Fixture.py','-sv'])
    # 同时运行的话,到CMD窗口输入: pytest -s test_pytest_Fixture.py test_pytest_Fixture2.py test_pytest_Fixture3.py(3在子文件夹,找不到)


test_pytest_Fixture2.py(同目录文件)

# -*- coding: utf-8 -*-
#test_pytest_Fixture2.py

import pytest

# -----------------------------------@Fixture作用范围:scope = 'session'    多文件使用------------------------------------------------
# 如果在不同模块下(.py文件里),session是给多个.py文件使用,并且写到conftest.py文件里,conftest.py文件名称是固定的,pytest会自动识别该文件。
# 放到工程的根目录下,就可以全局调用了,如果放到某个package包下,那就只在该package内有效,如同上面的例子

# 在该文件目录下新建conftest.py文件:

# 本文件调用
class TestLogin2():
    def test_4(self,login):     # 跨文件调用conftest.py 内的Fixture,无需引用      》》》     输入账号密码conftest.py[login]      》》》     用例4PASSED
        print("用例4")
    def test_5(self):
        print("用例5")          # 没有调用的用例方法                  》》》     用例5PASSED
    def test_6(self,login):
        print("用例6")          # 仅单文件执行的话,用例3 执行结束就会执行login的后置方法      》》》     用例6PASSED      》》》     已退出登录,清理数据完成






# pytest.main()  默认运行当前文件所在目录下所有符合条件的case
# pytest.main(["文件名/目录地址"])  运行指定文件/或目录下的所有case
# pytest main(["文件名::类名"])   运行指定类
# pytest main(["文件名::类名::方法名"])  运行指定方法
# pytest main(["文件名::函数名"]) 运行指定函数
if __name__ == '__main__':
    pytest.main(['test_pytest_Fixture2.py','-sv'])
    # 同时运行的话,到CMD窗口输入: pytest -s test_pytest_Fixture.py test_pytest_Fixture2.py test_pytest_Fixture3.py(3在子文件夹,找不到)

//test_pytest_Fixture3.py(下级目录文件,需要放__init__.py)

# -*- coding: utf-8 -*-
#test_pytest_Fixture3.py

import pytest

# -----------------------------------@Fixture作用范围:scope = 'session'    多文件使用------------------------------------------------
# 如果在不同模块下(.py文件里),session是给多个.py文件使用,并且写到conftest.py文件里,conftest.py文件名称是固定的,pytest会自动识别该文件。
# 放到工程的根目录下,就可以全局调用了,如果放到某个package包下,那就只在该package内有效,如同上面的例子

# 在该文件目录下新建conftest.py文件:

# 本文件调用,以参数的方式代入
class TestLogin3():
    def test_7(self,login):     # 跨文件调用conftest.py 内的Fixture,无需引用      》》》     输入账号密码conftest.py[login]      》》》     用例7PASSED
        print("用例7")
    def test_8(self,login):
        print("用例8")          # 没有调用的用例方法                  》》》     用例8PASSED
    def test_9(self):
        print("用例9")          # 仅单文件执行的话,整个文件模块执行结束才会执行login的后置方法      》》》     用例9PASSED      》》》     已退出登录,清理数据完成


# 我们在conftest.py中声明完前置函数后,在测试模块中除了使用传入函数名的方式,还可以使用@pytest.mark.userfixtures()装饰器。

class TestLogin4():
    @pytest.mark.usefixtures('login')
    def test_10(self):
        print("用例10")
    @pytest.mark.usefixtures('login')
    def test_11(self):
        print("用例11")
    def test_12(self):
        print("用例12")

# pytest.main()  默认运行当前文件所在目录下所有符合条件的case
# pytest.main(["文件名/目录地址"])  运行指定文件/或目录下的所有case
# pytest main(["文件名::类名"])   运行指定类
# pytest main(["文件名::类名::方法名"])  运行指定方法
# pytest main(["文件名::函数名"]) 运行指定函数
if __name__ == '__main__':
    pytest.main(['test_pytest_Fixture3.py','-sv'])
    # 同时运行的话,到CMD窗口输入: pytest -s test_pytest_Fixture.py test_pytest_Fixture2.py test_pytest_Fixture3.py(3在子文件夹,找不到)

conftest.py (根目录文件,可向下检索执行)

# -*- coding: utf-8 -*-
# conftest.py这个文件名是固定的,不可以更改
# conftest.py与运行用例在同一个包下,并且该包中有__init__.py文件
# 使用的时候不需要导入conftest.py,会自动寻找。
# conftest.py文件的作用域是当前包内(包括子包);如果函数调用固件优先从当前测试类中寻找,然后是模块(.py文件)中,接着是当前包中寻找(conftest.py中),如果没有再找父包直至根目录;如果我们要声明全局的conftest.py文件,我们可以将其放在根目录下。

#  conftest.py作用范围:测试类 > .py文件 > package

# fixture 固定装饰器,作用域:scope = 'session'
import pytest
@pytest.fixture(scope='session')
def login():
    print("输入账号密码,conftest.py")
    yield
    print("已退出登录,清理数据完成,conftest.py")


# 4.autouse=True自动使用,有配置的,所有test_开头的文件前后,都会使用
#@pytest.fixture(autouse=True, scope="session")
@pytest.fixture(scope="session")
def login_auto():
    print("输入账号密码,conftest.py autouse        配置再conftest的只能文件前后执行前后置,无法每个test_方法执行,需要在文件内使用fixture")
    yield
    print("已退出登录,清理数据完成,conftest.py")


posted @ 2022-12-20 11:08  Topi  阅读(86)  评论(0)    收藏  举报