使用 pytest 标记的预测试任务?
Posted
技术标签:
【中文标题】使用 pytest 标记的预测试任务?【英文标题】:Pre-test tasks using pytest markers? 【发布时间】:2021-04-02 16:51:31 【问题描述】:我有一个使用 pytest 的 Python 应用程序。对于我的几个测试,有对 Elasticsearch 的 API 调用(使用 elasticsearch-dsl-py)会减慢我想要的测试:
-
除非使用 Pytest 标记,否则应避免。
如果使用标记,我希望该标记在测试运行之前执行一些代码。就像使用
yield
时夹具的工作方式一样。
这主要是受到 pytest-django 的启发,您必须使用 django_db
标记才能连接到数据库(但如果您尝试连接到数据库,它们会抛出错误,而我只是不这样做'一开始就不想打电话,仅此而已)。
例如:
def test_unintentionally_using_es():
"""I don't want a call going to Elasticsearch. But they just happen. Is there a way to "mock" the call? Or even just prevent the call from happening?"""
@pytest.mark.elastic
def test_intentionally_using_es():
"""I would like for this marker to perform some tasks beforehand (i.e. clear the indices)"""
# To replicate that second test, I currently use a fixture:
@pytest.fixture
def elastic():
# Pre-test tasks
yield something
我认为这是标记的用例,对吧?主要受 pytest-django 启发。
【问题讨论】:
@pytest.mark.usefixtures("elastic")
?
对于模拟弹性搜索,请查看例如elasticmock
感谢@hoefling 啊,我认为usefixtures
装饰器非常适合第二种情况,而不是标记。但是我如何在默认情况下阻止对 ES 的调用(如没有应用标记/夹具时)?
我想我现在明白你的意思了,很快就会添加答案。
【参考方案1】:
您最初使用夹具和自定义标记组合的方法是正确的;在下面的代码中,我从您的问题中提取了代码并填补了空白。
假设我们有一些使用官方elasticsearch
客户端的虚拟函数进行测试:
# lib.py
from datetime import datetime
from elasticsearch import Elasticsearch
def f():
es = Elasticsearch()
es.indices.create(index='my-index', ignore=400)
return es.index(
index="my-index",
id=42,
body="any": "data", "timestamp": datetime.now(),
)
我们添加两个测试,一个没有标记elastic
,应该在假客户端上操作,另一个标记,需要访问真实客户端:
# test_lib.py
from lib import f
def test_fake():
resp = f()
assert resp["_id"] == "42"
@pytest.mark.elastic
def test_real():
resp = f()
assert resp["_id"] == "42"
现在让我们编写 elastic()
夹具,它将根据是否设置了 elastic
标记来模拟 Elasticsearch
类:
from unittest.mock import MagicMock, patch
import pytest
@pytest.fixture(autouse=True)
def elastic(request):
should_mock = request.node.get_closest_marker("elastic") is None
if should_mock:
patcher = patch('lib.Elasticsearch')
fake_es = patcher.start()
# this is just a mock example
fake_es.return_value.index.return_value.__getitem__.return_value = "42"
else:
... # e.g. start the real server here etc
yield
if should_mock:
patcher.stop()
注意autouse=True
的用法:fixture 将在每次测试调用时执行,但只有在测试未标记时才进行修补。通过request.node.get_closest_marker("elastic") is None
检查标记的存在。如果您现在运行这两个测试,test_fake
将通过,因为elastic
模拟了Elasticsearch.index()
响应,而test_real
将失败,假设您没有在端口 9200 上运行服务器。
【讨论】:
对这个答案超级满意!!!我从来没有学会如何正确使用模拟和补丁。我认为这是一个足够好的答案,可以站稳脚跟,因此被接受。我想我唯一担心的是你在嘲笑一个特定的退货案例。我希望有一种方法来阻止调用,但我认为 Elasticsearch 模拟库可能会这样做。非常感谢! 是的,如果我错了,请纠正我——我想这更像是对自己的一个说明——但使用 Elasticmock 库可能意味着不使用他们的装饰器,而是使用_get_elasticmock
进行修补。 github.com/vrcmarcos/elasticmock/blob/…
@acw 你也可以只做patcher = patch('lib.Elasticsearch', FakeElasticsearch)
; _get_elasticmock
只是一种缓存假客户端以供重用的方法。我同意elasticmock
的公共 API 虽然非常简洁,但可以提供更多模拟可能性。以上是关于使用 pytest 标记的预测试任务?的主要内容,如果未能解决你的问题,请参考以下文章