pytest框架中前置和后置的学习总结:1、setup和teardown的使用2、confest.py文件中fixture的使用

一、概述

介绍pytest框架中的前置和后置之前,先来介绍一个测试场景,比如我们要测试淘宝的购物车的添加功能,那我们是不是需要先登录,然后再到购物车模块去操作,最后是退出账号,那么像这种测试一个模块前后需要做的准备工作和收尾的工作,可以通过写代码实现,但是pytest帮我们提供了两种方法:
1、通过teardown和setup来控制前置和后置执行的位置。
2、通过在conftest.py文件中利用在函数上加@pytest.fixture()装饰器的方法来实现。
作用范围区别:
1是仅在当前的所在的模块中生效。
2是可以在多个.py文件中共享,或者是多个包以及整个测试项目下被共享。

二、通过setup_xx 和 teardown_xx来实现前置和后置

根据用例运行级别可以分为以下几种
模块级(setup_module/teardown_module)开始于模块始末,全局的

函数级(setup_function/teardown_function)只对函数用例生效(不在类中)

类级(setup_class/teardown_class)只在类中前后运行一次(在类中)

方法级(setup_method/teardown_method)开始于方法始末(在类中)

类里面的(setup/teardown)运行在调用方法的前后
举例说明:
一、函数级别的。

import pytest
count = 1

def setup_function():
    print("函数测试开始了.....")

def teardown_function():
    print("函数测试结束了.....")

def add(x, y):
    if type(x) is int or type(y) is int:
        return x+y
    else:
        raise TypeError
# @pytest.mark.ToDo3

def test_case1():
    print("正常值。。。")
    assert add(1,2) == 3

# @pytest.mark.toDo2
# @pytest.mark.skipif(count <= 1, reason = "count值太小")
def test_case2():
    print("异常值")
    with pytest.raises(TypeError):
        assert add(1,"2") != 3

if __name__ == '__main__':
    pytest.main(['-sq',  'test_10_21.py'])

运行结果,可以看到:每个测试函数执行前和执行后都会去执行一遍。

C:\Python36\python.exe D:/自动化B/python_test/test_10_21.py
函数测试开始了.....
正常值。。。
.函数测试结束了.....
函数测试开始了.....
异常值
.函数测试结束了.....
sss
2 passed, 3 skipped in 0.04s

Process finished with exit code 0

二、模块级别的。
将上面的那两行代码改成下面两行:

def setup_module():
    print("模块测试开始了.....")

def teardown_module():
    print("模块测试结束了.....")

运行结果:

函数测试开始了.....
正常值。。。
.异常值
.sss函数测试结束了.....

2 passed, 3 skipped in 0.06s

可以看到,在整个模块运行前和运行结束后分别执行了一次。

三、类和方法(这里需要注意一下,这个是放在类中的。)

import pytest
count = 1
def setup_module():
    print("模块测试开始了.....")

def teardown_module():
    print("模块测试结束了.....")
    
def add(x, y):
    if type(x) is int or type(y) is int:
        return x+y
    else:
        raise TypeError
# @pytest.mark.ToDo3

def test_case1():
    print("正常值。。。")
    assert add(1,2) == 3

# @pytest.mark.toDo2
# @pytest.mark.skipif(count <= 1, reason = "count值太小")
def test_case2():
    print("异常值")
    with pytest.raises(TypeError):
        assert add(1,"2") != 3
# @pytest.mark.skip(reason = "我不想被执行")
class TestCase(object):
    def setup_class(self):
        print("类测试开始了.....")

    def teardown_class(self):
        print("类测试结束了.....")

    def setup_method(self):
        print("方法测试开始了.....")

    def teardown_method(self):
        print("方法测试结束了.....")

    def setup(self):
        print("方法调用测试开始了.....")

    def teardown(self):
        print("方法调用测试结束了.....")
    def test_CASE3(self):
        print("特殊字符:")
        with pytest.raises(TypeError):
            assert add("$", "****") != 3

@pytest.mark.skip()
def test_error():
    print("这是一个错误结果的用例测试")
    assert 1 == 2
@pytest.mark.skip()
def test_CASE4():
    print("特殊字符:")
    with pytest.raises(TypeError):
        assert add("][]", ">?>") != 3


if __name__ == '__main__':
    pytest.main(['-sv',  'test_10_21.py'])

运行结果:

模块测试开始了.....
正常值。。。
.异常值
.类测试开始了.....
方法测试开始了.....
方法调用测试开始了.....
特殊字符:
.方法调用测试结束了.....
方法测试结束了.....
类测试结束了.....
ss模块测试结束了.....

3 passed, 2 skipped in 0.07s

三、通过conftest.py文件中添加@pytest.fixture的方式

首先介绍下conftest,py文件吧,conftest.py文件名是固定的命名方式,不可改变,位置可以放在根目录下,也可以放在模块下,放在不同的位置,则在执行测试用例的时候被执行的顺序也是不一样的。放在根目录下,则会在首次执行根目录下的setup.py的时候会被执行一次。然后接着去执行包下的conftest.py(前提是包下面放了conftest.py)
从上面看,正式因为conftest。py文件只要有,每次执行用例的时候会自动加载这个文件,那么是不是跟前置和后置有点像,所以就有人想到了在这个文件中放置@pytest.fixture,通过scope来控制作用的范围:

@pytest.fixture(scope=“function”, params=None, autouse=False, ids=None, name=None)

 1. scope参数:
scope:有四个级别参数 "function" (默认), "class", "module" or "session"
scope参数为session,那么所有的测试文件执行前执行一次
scope参数为module,那么每一个测试文件执行前都会执行一次
scope参数为class,那么每一个测试文件中的测试类执行前都会执行一次
scope参数为function,那么每一个测试文件中的每一个测试用例执行前都会执行一次
 2. params参数:
默认是None,也可以传参数,传多个值时,则会执行多次测试用例,这样就实现了参数化。
@pytest.fixture(scope="function",params=[1,2])
def data(request):
	return request.param
def test_add(data):#会被执行两次,第一次传入的data=1,第二次传入的data=2
	assert 1==data
 3. autouse参数
 默认是False,只有传入参数名称才会被调用,当开启时,则不管有没有将函数作为参数传入到测试用例中,都会被执行。
 ids和name很少用,这里不重点介绍了。。。感兴趣可以自行查找相关资料了解。。

使用的方法是在测试用例的类上通过@pytest.mark.usefixture()
或者直接在方法的参数中出传入被@pytest.fixture()装饰的函数名即可。

以上介绍的都是fixture实现前置,那么后置怎么实现呢?
接下来介绍一下yeild关键字,这个应该学过python的都不陌生了吧,生成器中就是使用的这个关键字来实现的,感兴趣的小伙伴可以去查看我这篇文章中有介绍:https://blog.csdn.net/weixin_43726471/article/details/120600966
那yield和return有什么区别呢?

@pytest.fixture(scope="function")
def yied_ex():
	print("前置工作") #step1
	yield "run...." #step2
	print("后置工作")#step4

def test_yield(yied_ex):
	print("返回什么{}".format(yied_ex))#step3

运行结果:

test0205.py::test_yield 前置工作
返回什么run....
PASSED后置工作

相同点:
yield和return都是作为函数的返回
区别:
return返回,函数就结束了,并且return后面不能跟代码了,否则会报语法错误。
但是yield后面还是可以跟代码,遇到yield就停止了,返回yield后的数据,然后回到函数调用的地方,执行完之后,会再次回到yield这里,继续执行yield后面的语句。

四、总结

为什么有setup和teardown,还要出现fixture?
像如下这种场景,就比较适合用fixture,setup和teardown无法满足。
一个.py文件中,有三个测试用例,其中测试用例1和测试用例3需要获得登陆的token,测试用例2不需要,那么可以在测试用例1和测试用例3中传入函数名,2不用传,则不会被执行。

@pytest.fixture(scope="function")
def login():
	return "token"

def test_01(login):
	print("返回什么{}".format(login))

def test_02():
	assert 1==1

def test_03(login):
	print("返回什么{}".format(login))

运行结果:

test0205.py::test_01 返回什么token
PASSED
test0205.py::test_02 PASSED
test0205.py::test_03 返回什么token
PASSED

如果使用setup来实现的话:

def setup_function():
	print("前置准备。。。")
def test_01():
	# print("返回什么{}".format(login))
	pass

def test_02():
	assert 1==1

def test_03():
	# print("返回什么{}".format(login))
	pass

返回结果:

test0205.py::test_01 前置准备。。。
PASSED
test0205.py::test_02 前置准备。。。
PASSED
test0205.py::test_03 前置准备。。。
PASSED

可以看到全部被执行。

### 使用 Fixture 实现 Setup Teardown 在 `pytest` 中,通过使用 `fixture` 可以方便地实现测试用例的前置条件(setup后置处理(teardown)。这种方式不仅简化了代码结构,还提高了代码的可读性维护性。 #### 基本概念 - **Fixture**: 是一种特殊的函数,在测试执行前或后提供必要的环境准备或资源清理工作。 - **Scope**: 定义了 fixture 的生命周期,常见的有 `'function'`, `'class'`, `'module'`, `'session'`。 - **Yield**:fixture 函数中使用 `yield` 关键字分隔 setup teardown 部分。`yield` 之前的语句作为 setup 操作被执行;`yield` 后面的内容则会在对应的测试完成之后作为 teardown 被触发[^1]。 #### 示例代码展示 下面是一个简单的例子,展示了如何利用 `fixture` 来管理浏览器实例的生命期: ```python import pytest from selenium import webdriver @pytest.fixture(scope="function", autouse=True) def browser(): """创建并返回 WebDriver 对象""" print("\nStarting Chrome Browser...") driver = webdriver.Chrome() yield driver # 浏览器关闭逻辑放在 yield 后面 print("\nClosing Chrome Browser.") driver.quit() def test_search(browser): """打开百度首页,并验证页面标题包含 '百度' """ url = "https://www.baidu.com" expected_title = "百度" browser.get(url) actual_title = browser.title assert expected_title in actual_title, f"Expected '{expected_title}' but got '{actual_title}'" ``` 在这个例子中,定义了一个名为 `browser` 的 fixture,其作用域设定为每次测试函数(`scope="function"`), 并且自动应用于所有测试 (`autouse=True`)。当任何依赖此 fixture 的测试开始时,会先启动一个新的 Chrome 浏览器实例;而在该次测试结束后,则会自动调用 `driver.quit()` 方法来终止这个浏览器进程[^4]。 #### 更复杂的场景 对于更复杂的应用程序状态初始化或者外部服务连接等情况,也可以采用类似的模式来进行封装。比如数据库连接池、API客户端配置等都可以放入到 fixtures 当中去统一管理释放资源[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

如梦@_@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值