Python测试框架之Pytest
Pytest 是 Python 中一个功能强大、灵活、易于上手的单元测试框架。它支持简单的函数式测试,也支持复杂的功能性测试和插件扩展,广泛用于个人项目、开源项目乃至大型企业项目中。以下是对 pytest 的详细介绍。
pip install -U pytest
1 基本规则
在pytest框架中,有如下约束:
- 所有的单测文件名都需要满足
test_*.py格式或*_test.py格式。 - 在单测文件中,测试类以Test开头,并且不能带有 init 方法(注意:定义class时,需要以T开头,不然pytest是不会去运行该class的)
- 在单测类中,可以包含一个或多个test_开头的函数。
- 断言必须使用 assert
- 此时,在执行pytest命令时,会自动从当前目录及子目录中寻找符合上述约束的测试函数来执行。
一般采用命令行模式运行测试文件
# pytest 文件路径/测试文件名
pytest test_module.py::TestClass::test_method
pytest -s test_soloman.py::TestSolomanClass::test_soloman_method
# 显示print内容,在运行测试脚本时,为了调试或打印一些内容,我们会在代码中加一些print内容,但是在运行pytest时,这些内容不会显示出来。如果带上-s,就可以显示了。
pytest -s test_solopman.py
2 @pytest.fixture
fixture修饰器来标记固定的工厂函数,在其他函数,模块,类或整个工程调用它时会被激活并优先执行,通常会被用于完成预置处理和重复操作。在@pytest.fixture函数中实现初始化操作,那么用例执行完之后如需要清除数据(或还原)操作,可以使用 yield 来实现。
# test_abc.py
class Test_ABC:
# 使用 yield 来实现创建obj,使用之后再将其删除掉
@pytest.fixture(scope="function")
def before(self):
print("------->before")
obj = Soloman(name = "Soloman", website = "www.soloman.vip")
obj.save()
yield obj
obj.delete()
# test_a方法传入了被fixture标识的函数,以变量的形式
def test_a(self, before):
print("------->test_a")
assert 1
# 调用pytest的main函数执行测试,一般用命令行:pytest -s test_abc.py
if __name__ == '__main__':
pytest.main("-s test_abc.py")
@pytest.fixture 作用范围
def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
"""
:arg scope: 可选四组参数:function(默认)、calss、module、package/session
:arg params: 一个可选的参数列表,它将导致多个参数调用fixture函数和所有测试使用它。
:arg autouse: 如果为True,则fixture func将为所有测试激活可以看到它。如果为False(默认值),则需要显式激活fixture。
:arg ids: 每个参数对应的字符串id列表,因此它们是测试id的一部分。如果没有提供id,它们将从参数中自动生成。
:arg name: fixture的名称。 这默认为被装饰函数的名称。
"""
说下 scope 四组参数的意义:
-
function:每个方法(函数)都会执行一次。
-
class:每个类都会执行一次。类中有多个方法调用,只在第一个方法调用时执行。
-
module:一个 .py 文件执行一次。一个.py 文件可能包含多个类和方法。
-
package/session:多个文件调用一次,可以跨 .py 文件。
3 Pytest的setup和teardown函数
- setup,在测试函数或类之前执行,完成准备工作,例如数据库链接、测试数据、打开文件等
- teardown,在测试函数或类之后执行,完成收尾工作,例如断开数据库链接、回收内存资源等
- setup和teardown主要分为:模块级,类级,功能级,函数级。
- 备注:也可以通过上面提及的yield,在fixture函数中通过yield实现setup和teardown功能
3.1 函数级setup和teardown
import pytest
class Test_ABC:
# 函数级开始
def setup(self):
print("------->setup_method")
# 函数级结束
def teardown(self):
print("------->teardown_method")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
if __name__ == '__main__':
pytest.main("-s test_abc.py")
# 函数级setup和teardown,运行于测试方法的始末,即:运行一次测试函数会运行一次setup和teardown
# 运行结果:setup_method test_a teardown_method setup_method test_b teardown_method
3.2 类级setup和teardown
import pytest
class Test_ABC:
# 测试类级开始
def setup_class(self):
print("------->setup_class")
# 测试类级结束
def teardown_class(self):
print("------->teardown_class")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
if __name__ == '__main__':
pytest.main("-s test_abc.py")
# 类级setup和teardown,运行于测试类的始末,即:在一个测试类只运行一次setup_class和teardown_class,不关心测试类内有多少个测试函数。
# 运行结果:setup_class test_a test_b teardown_class
4 conftest.py 配置文件
若有多个.py 文件需要调用某个被@pytest.fixture装饰的方法,可以把该方法方法写在 conftest.py 配置文件中。test_xxx.py 测试文件中调用时无需 import conftest,pytest 会自动搜索同级目录中的 conftest.py 文件
5 通过pytest.mark对test方法分类执行
# 通过@pytest.mark控制需要执行哪些feature的test,例如在执行test前增加修饰@pytest.mark.website。通过 -m "website" 执行有website标记的测试用例
pytest -m website test_soloman.py
# 执行没有website标记的test测试用例
pytest -m not website test_soloman.py
# 这条命令会执行被装饰器 @pytest.mark.slow 装饰的所有测试用例
pytest -m slow test_soloman.py