pytest--测试用例调用fixture、fixture传递测试数据(三)
1、简介
在pytest中,fixture是一种用于在测试前后进行预备和清理工作的代码处理机制。它有独立的命名,并通过声明激活。与unittest中的setup和teardown相比,fixture有显著改进,包括:
- 命名方式灵活,不局限于 setup 和 teardown 这几个命名
- conftest.py 配置里可以实现数据共享,不需要 import 就能自动找到 fixture
- scope=“module” 可以实现多个 .py 跨文件共享前置
- scope=“session” 可以实现多个 .py 跨文件使用一个 session 来完成多个用例
2、fixture参数解释
# -*- coding: utf-8 -*-
# @Time : 2023/11/21 14:57
# @Author : 梗小旭
# @File : conftest.py
import pytest
@pytest.fixture(scope="function", params=None, autouse=False, ids=None, name=None)
def login_page():
print("开始登录...")
- scope:可以理解成 fixture 的作用范围,默认:function,还有 class、module、package、session
- function 的作用域:每一个函数或方法都会调用
- class 的作用域:每一个类调用一次,一个类中可以有多个方法
- module 的作用域:每一个 .py 文件调用一次,该文件内又有多个 function 和 class
- session 的作用域:是多个文件调用一次,可以跨 .py 文件调用,每个 .py 文件就是 module
- params:一个可选的参数列表,它将导致多个参数调用 fixture 功能和所有测试使用它
- autouse:默认:False,需要用例手动调用该 fixture;如果是 True,所有作用域内的测试用例都会自动调用该 fixture
- ids:每个字符串 id 的列表,每个字符串对应于 params,这样他们就是测试ID的一部分。如果没有提供ID,它们将从 params 自动生成
- name:默认:装饰器的名称,同一模块的 fixture 相互调用建议写不同的名称
3、快速上手
我们先创建一个test_query.py文件,内容如下:
# -*- coding: utf-8 -*-
# @Time : 2023/11/21 14:55
# @Author : 梗小旭
# @File : test_query.py
import pytest
@pytest.fixture(scope="function")
def login():
print("登录成功。。。")
return {"token": "我是token"}
def test_query_success(login):
token = login
print(token)
print("查询成功。。。")
def test_query_fail():
print("查询失败。。。。")
if __name__ == '__main__':
pytest.main(["-s", "-q", "test_query.py"])
正常在做接口自动化时,肯定都需要先登录才能进行增删改查,所以前置条件得先登录,上述例子中使用pytest.fixture()定义了一个登录前置条件并返回token信息。test_query_success用例使用了login前置,test_query_fail未调用。那我们看下执行结果是什么:
从执行结果可以看出,test_query_success用例执行前调用了login函数,接收了login返回的token并打印了出来,而test_query_fail未调用就没有执行。这样就弥补了之前用setup和teardown的不足,我想让哪个用例执行这个前置,就可以控制了,而不是所有的用例都执行这个前置,更加的灵活。
得出结论:
@pytest.fixture() 装饰器用于声明函数是一个 fixture,如果测试用例的参数中包含此 fixture 函数,则在测试用例运行前会先运行此 fixture 函数。如果 fixture 函数有返回值,则将返回值传递给测试用例函数。
4、多种情况下使用
4.1、测试用例调用fixture的3种方式
- 将fixture装饰的函数名称作为测试用例的输入参数,例如上面例子中的login函数是test_query_success的输入参数
- 测试用例加上装饰器:@pytest.mark.usefixtures(fixture_name)
- fixture设置autouse=True
还是用上面test_query.py文件,代码如下:
# -*- coding: utf-8 -*-
# @Time : 2023/11/21 14:55
# @Author : 梗小旭
# @File : test_query.py
import pytest
@pytest.fixture(scope="function")
def login():
print("登录成功。。。")
# 调用方式一
def test_query_success_1(login):
print("调用方式一:查询成功。。。")
# 调用方式二
@pytest.mark.usefixtures("login")
def test_query_success_2():
print("调用方式二:查询成功。。。")
# 调用方式三
@pytest.fixture(autouse=True)
def login_autouse():
print("执行login_autouse,每个用例之前都会调用。。。")
@pytest.mark.usefixtures("login_autouse")
def query_success_3():
print("调用方式三:查询成功。。。")
if __name__ == '__main__':
pytest.main(["-s", "-q", "test_query.py"])
从上述代码可以看出分别用三种方式调用fixture,方式一直接拿函数名传参,方式二使用@pytest.mark.usefixtures装饰器,方式三使用 fixture设置autouse=True,但是query_success_3用例的函数名前面没有加test_,下面我们看下执行结果:
从执行结果可以得出下面几点:
- test_query_success_1引用了login,先执行login,再执行test_query_success_1
- test_query_success_2使用了装饰器,先执行了login,再执行了test_query_success_2
- fixture设置了autouse=True,每个用例都会执行,且用例不需要进行传参和使用装饰器都会执行,且优先级高于本身传入的前置条件
- test_query_success_3即使使用了装饰器,但是用例没有加test_开头,所以不会执行
如果一个用例我想使用多个前置,一个类里面的测试用例都使用一个fixture等多种情况呢,想必大家在写接口自动化的时候会遇到很多复杂场景,下面列出几个例子:
# -*- coding: utf-8 -*-
# @Time : 2023/11/21 14:55
# @Author : 梗小旭
# @File : test_query.py
"""
一个测试用例传入多个前置,删除商品需要先登录、找到商品ID,再通过商品ID删除商品。
需要注意的是,测试用例传入login, query的顺序不能错,执行规则是放在前面的先执行
"""
import pytest
@pytest.fixture(scope="function")
def login():
print("登录成功。。。")
@pytest.fixture(scope="function")
def query():
print("获取商品ID成功。。。")
def test_del_goods(login, query):
print("删除商品成功。。。")
if __name__ == '__main__':
pytest.main(["-s", "-q", "test_query.py"])
# -*- coding: utf-8 -*-
# @Time : 2023/11/21 14:55
# @Author : 梗小旭
# @File : test_query.py
"""
和上面的例子一样,但是使用@pytest.mark.usefixtures,同样也是先执行前面的。
"""
import pytest
@pytest.fixture(scope="function")
def login():
print("登录成功。。。")
@pytest.fixture(scope="function")
def query():
print("获取商品ID成功。。。")
@pytest.mark.usefixtures("login", "query")
def test_del_goods():
print("删除商品成功。。。")
if __name__ == '__main__':
pytest.main(["-s", "-q", "test_query.py"])
# -*- coding: utf-8 -*-
# @Time : 2023/11/21 14:55
# @Author : 梗小旭
# @File : test_query.py
"""
在类上面使用@pytest.mark.usefixtures,类下面所有的用例执行前都会调用login
"""
import pytest
@pytest.fixture(scope="function")
def login():
print("登录成功。。。")
@pytest.mark.usefixtures("login")
class TestQuery:
def test_query_1(self):
print("test_query_1查询成功。。。")
def test_query_2(self):
print("test_query_2查询成功。。。")
if __name__ == '__main__':
pytest.main(["-s", "-q", "test_query.py"])
关键点:
- 在类声明上面加 @pytest.mark.usefixtures() ,代表这个类里面所有测试用例都会调用该 fixture
- 可以叠加多个 @pytest.mark.usefixtures() ,先执行的放底层,后执行的放上层
- 可以传多个 fixture 参数,先执行的放前面,后执行的放后面
- 如果 fixture 有返回值,用 @pytest.mark.usefixtures() 是无法获取到返回值的,必须用传参的方式(方式一)
4.2、fixture传递测试数据
通过上面的例子,其实我们已经能看出fixture如何传参了,当我们把fixture装饰的函数名称传给测试用例后,执行用例时会先执行fixture装饰的函数并返回值,在测试用例中我们直接可以拿变量接收返回值,这里必须再提一下,使用@pytest.mark.usefixtures()是拿不到返回值的。
# -*- coding: utf-8 -*-
# @Time : 2023/11/21 14:55
# @Author : 梗小旭
# @File : test_query.py
import pytest
@pytest.fixture(scope="function")
def login():
print("登录成功。。。")
return {"token": "我是token"}
def test_query(login):
token = login
print(token)
print("查询成功。。。")
if __name__ == '__main__':
pytest.main(["-s", "-q", "test_query.py"])
执行结果如下:
这章节目前只讲了测试用例调用fixture、fixture传递测试数据部分,fixture的其他部分在后面章节继续讲解,如果觉得博主的文章对你有所帮助,点个关注支持一下哦,ღ( ´・ᴗ・` )比心