5 分钟快速上手 pytest 测试框架
Python中文社区
共 11649字,需浏览 24分钟
· 2021-04-20
为什么要做单元测试
print()
函数将其打印输出到控制台上。def myfunc(*args, **kwargs):
do_something()
data = ...
print(data)
print()
函数来确保结果的准确性,但同时,也由于要测试的模块或者函数变多,代码中也会逐渐遗留着各种未被去掉或注释的 print()
调用,让整个代码变得不是那么简洁得体。print()
进行测试的步骤统一地放到单元测试中来进行。unittest
。但对于新手来说,unittest
在学习曲线上是稍微有点难度的,因为是需要通过继承测试用例类(TestCase
)来进行封装,所以需要对面向对象的知识有足够多的了解;而和类绑定在一起就意味着如果想要实现定制化或者模块解耦,可能就需要多花一些时间在设计划分上。TestCase
来实现我们的测试,我们只需要简单到保持我们原有的代码逻辑不变,外加一个 assert
关键字来断言结果,剩下的部分 pytest 会帮我们处理。# main.py
import pytest
raw_data = read_data(...)
def test_myfunc(*args, **kwargs):
do_something()
data = ...
assert data == raw_data
if __name__ == '__main__':
pytest.main()
main.py
文件,就能在终端控制台上看到 pytest 为我们测试得到的结果。如果结果通过,则不会有过多的信息显示,如果测试失败,则会抛出错误信息并告知运行时 data
里的内容是什么。快速实现你的第一个 Pytest 测试
pip install pytest
安装 pytest 之后,我们就可以快速实现我们的第一个测试。test_main.py
命名,然后当中留存如下内容:from typing import Union
import pytest
def add(
x: Union[int, float],
y: Union[int, float],
) -> Union[int, float]:
return x + y
@pytest.mark.parametrize(
argnames="x,y,result",
argvalues=[
(1,1,2),
(2,4,6),
(3.3,3,6.3),
]
)
def test_add(
x: Union[int, float],
y: Union[int, float],
result: Union[int, float],
):
assert add(x, y) == result
pytest -v
,就会看到 pytest 已经帮我们将待测试的参数传入到测试函数中,并实现对应的结果:for
循环传参,并且还能直观地从结果中看到每次测试中传入参数的具体数值是怎样。这里我们只通过 pytest 提供的 mark.parametrize
装饰器就搞定了。也说明 pytest 的上手程度是比较容易的,只不过我们需要稍微了解一下这个框架中的一些概念。Pytest 概念与用法
命名
test_*
开头或是以 *_test
结尾,这是为了遵守标准的测试约定。如果我们将前面快速上手的例子文件名中的 test_
去掉,就会发现 pytest 没有收集到对应的测试用例。# content of pytest.ini
# Example 1: have pytest look for "check" instead of "test"
[pytest]
python_files = check_*.py
python_classes = Check
python_functions = *_check
pytest test.py::test_demo
pytest test.py::TestDemo::test_demo
标记(mark)
mark
标记是一个十分好用的功能,通过标记的装饰器来装饰我们的待测试对象,让 pytest 在测试时会根据 mark
的功能对我们的函数进行相应的操作。mark
功能,我们只挑常用的说。参数测试:pytest.parametrize
mark.parametrize
主要就是用于我们想传递不同参数或不同组合的参数到一个待测试对象上的这种场景。test_add()
示例一样,分别测试了:当 x=1
且y=1
时,结果是否为result=2
的情况当 x=2
且y=4
时,结果是否为result=6
的情况当 x=3.3
且y=3
时,结果是否为result=6.3
的情况……
import pytest
@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
@pytest.mark.parametrize("result", [2, 4])
def test_add(x, y, result):
assert add(x,y) == result
parametrize
中,pytest 依旧能帮我们把所有情况都给测试一遍。这样我们就再也不用写多余的代码。parametrize
和我们后面将要讲到的一个重要的概念 fixture
会有一些差异:前者主要是模拟不同参数下时待测对象会输出怎样的结果,而后者是在固定参数或数据的情况下,去测试会得到怎样的结果。跳过测试
add()
,但当版本大于 Python 3.3 时使用必然会出现问题。mark.skip
和 mark.skipif
两个标记,当然后者用的更多一些。import pytest
import sys
@pytest.mark.skipif(sys.version_info >= (3,3))
def test_add(x, y, result):
assert add(x,y) == result
sys
模块判断 Python 解释器的版本是否大于 3.3,大于则会自动跳过。预期异常
def div(x, y):
return x / y
y=0
时,必然会引发 ZeroDivisionError
异常。所以通常的做法要么就用 try...exception
来捕获异常,并且抛出对应的报错信息(我们也可以使用 if
语句进行条件判断,最后也同样是抛出报错):def div(x, y):
try:
return x/y
except ZeroDivisionError:
raise ValueError("y 不能为 0")
raises()
方法:import pytest
@pytest.mark.parametrize("x", [1])
@pytest.mark.parametrize("y", [0])
def test_div(x, y):
with pytest.raises(ValueError):
div(x, y)
ZeroDivisionError
后我们自己指定抛出的 ValueError
,而非前者。当然我们可以使用另外一个标记化的方法(pytest.mark.xfail
)来和 pytest.mark.parametrize
相结合:
@pytest.mark.parametrize(
"x,y,result",
[
pytest.param(1,0, None, marks=pytest.mark.xfail(raises=(ValueError))),
]
)
def test_div_with_xfail(x, y, result):
assert div(x,y) == result
Fixture
fixture
。关于 fixture
的翻译大部分人都直接将其直译为了「夹具」一词,但如果你有了解过 Java Spring 框架的 那么你在实际使用中你就会更容易将其理解为 IoC 容器类似的东西,但我自己认为它叫「载具」或许更合适。fixture
的作用往往就是为我们的测试用例提供一个固定的、可被自由拆装的通用对象,本身就像容器一样承载了一些东西在里面;让我们使用它进行我们的单元测试时,pytest 会自动向载具中注入对应的对象。connect()
,接着进行操作,最后使用完之后断开连接 close()
以释放资源。# test_fixture.py
import pytest
class Database(object):
def __init__(self, database):
self.database = database
def connect(self):
print(f"\n{self.database} database has been connected\n")
def close(self):
print(f"\n{self.database} database has been closed\n")
def add(self, data):
print(f"`{data}` has been add to database.")
return True
@pytest.fixture
def myclient():
db = Database("mysql")
db.connect()
yield db
db.close()
def test_foo(myclient):
assert myclient.add(1) == True
@pytest.fixture
这一行装饰器代码,通过该装饰器我们可以直接使用一个带有资源的函数将其作为我们的载具,在使用时将函数的签名(即命名)作为参数传入到我们的测试用例中,在运行测试时 pytest 则会自动帮助我们进行注入。myclient()
中 db
对象的 connect()
方法调用模拟数据库连接的方法,在测试完成之后会再次帮我们调用 close()
方法释放资源。fixture
机制是一个让我们能实现复杂测试的关键,试想我们以后只需要写好一个带有测试数据的 fixture
,就可以在不同的模块、函数或者方法中多次使用,真正做到「一次生成,处处使用」。function
:函数作用域(默认)class
:类作用域module
:模块作用域package
:包作用域session
:会话作用域
@pytest.fixture()
中多增加一个 scope
参数,从而提升载具作用的范围。conftest.py
文件中进行统一管理:# conftest.py
import pytest
class Database:
def __init__(self, database):
self.database:str = database
def connect(self):
print(f"\n{self.database} database has been connected\n")
def close(self):
print(f"\n{self.database} database has been closed\n")
def add(self, data):
print(f"\n`{data}` has been add to database.")
return True
@pytest.fixture(scope="package")
def myclient():
db = Database("mysql")
db.connect()
yield db
db.close()
test_add()
测试部分稍微修改一下,无需显式导入 myclient
载具就可以直接注入并使用:from typing import Union
import pytest
def add(
x: Union[int, float],
y: Union[int, float],
) -> Union[int, float]:
return x + y
@pytest.mark.parametrize(
argnames="x,y,result",
argvalues=[
(1,1,2),
(2,4,6),
]
)
def test_add(
x: Union[int, float],
y: Union[int, float],
result: Union[int, float],
myclient
):
assert myclient.add(x) == True
assert add(x, y) == result
pytest -vs
即可看到输出的结果:Pytest 扩展
pip
命令安装即可,最后使用只需要简单的参照插件的使用文档编写相应的部分,最后启动 pytest 测试即可。pytest-xdist
-n <CPU_NUMBER>
参数即可,其中的 CPU 数量可以直接用 auto
代替,它会自动帮你调整 pytest 测试所使用的 CPU 核心数:pytest-asyncio
@pytest.mark.asyncio
标记装饰异步函数或方法,然后进行测试即可:import asyncio
import pytest
async def foo():
await asyncio.sleep(1)
return 1
@pytest.mark.asyncio
async def test_foo():
r = await foo()
assert r == 1
结语
作者:100gle,练习时长不到两年的非正经文科生一枚,喜欢敲代码、写写文章、捣鼓捣鼓各种新事物;现从事有关大数据分析与挖掘的相关工作。
赞 赏 作 者
更多阅读
特别推荐
点击下方阅读原文加入社区会员
评论
测试新人,如何快速上手一个陌生的系统!
大家好,我是狂师!作为刚入行不久的测试新人,面对一个陌生的系统时,可能会感到有些手足无措。面对一个全新的系统系统,如何快速上手并展开有效的测试工作是一个重要的挑战。本文将探讨测试新人如何通过一系列步骤和策略,快速熟悉并掌握新系统的测试要点,从而提高测试效率和质量。本文旨在为测试新手提供一份指导,帮助
测试开发技术
0
APP 安全测试项总结
一、安装包测试 1.1、关于反编译 目的是为了保护公司的知识产权和安全方面的考虑等,一些程序开发人员会在源码中硬编码一些敏感信息,如密码。而且若程序内部一些设计欠佳的逻辑,也可能隐含漏洞,一旦源码泄漏,安全隐患巨大。 为了避免这些问题,除了代码审核外,通常开发的做法是对代码进行混淆,混淆后源代
测试开发技术
0
自动化测试做得好的标准是什么
自动化测试要做得好的标准,主要包括以下几个方面:一、高覆盖率与精准定位1、测试用例覆盖全面:自动化测试应覆盖产品的核心功能、关键业务流程以及常见的异常场景,确保测试范围广泛,降低遗漏风险。2、问题定位准确:自动化测试应能够精准地识别并定位问题,包括缺陷的位置、产生的原因以及可能的影响,为开发团队提供
测试开发社区
0
21.3K star!推荐一款可视化自动化测试/爬虫/数据采集神器!功能免费且强大!
【温馨提示】由于公众号更改了推送规则,不再按照时间顺序排列,如果不想错过测试开发技术精心准备的的干货文章,请将测试开发技术设为“星标☆”,看完文章在文尾处点亮“在看”!大家好,我是狂师!在大数据时代,信息的获取与分析变得尤为重要。对于开发者、数据分析师乃至非技术人员来说,能够高效地采集网络数据并进行
测试开发技术
4
前端框架新势力大盘点
点击上方 前端Q,关注公众号回复加群,加入前端Q技术交流群近年来,前端领域快速发展,新的框架不断涌现,为开发者提供了更多选择和解决方案。尽管 React、Vue、Angular、Next.js、Preact 等老牌框架依然稳坐市场主流,但新势力前端框架的崛起也为特定场景带来了更佳的适配和优
前端Q
0
Spring Boot + flowable 快速实现工作流
关注我们,设为星标,每天7:40不见不散,架构路上与您共享回复架构师获取资源大家好,我是你们的朋友架构君,一个会写代码吟诗的架构师。来源:blog.csdn.net/zhan107876/article/details/120815560总览一、flowable-ui部署运行二、绘制流程图绘图细节:
Java架构师社区
0
2025年有望破万亿,AIoT助力下,物流行业正在迎来快速发展
作者:王飞鹏物联网智库 原创3月底,正在赶赴港股上市的菜鸟网络被阿里总部召回,上市进程按下了暂停键。在阿里去年定下“大拆分”战略后,菜鸟本是最有希望率先独立IPO的企业,但是在临门一脚之际,阿里却做出了不上市的决策。这一举动引发了外界热议。分析人士普遍认为,阿里之所以做出这一决策,很重要的一个原因是
物联网智库
0
110 个 Java 主流组件和框架整理,常用的应有尽有,建议收藏!!
点击关注公众号,Java 干货及时推送↓推荐阅读:铜三铁四,怒拿 35K * 14 薪!整理:四猿外以下排序是按照从技术组件到开发框架到代码工具,也有一些实在不好分类的,就放到最后了。WEB 容器Tomcathttps://tomcat.apache.org/Jettyhttps://ww
Java技术栈
0