简体   繁体   English

使用 pytest 标记的预测试任务?

[英]Pre-test tasks using pytest markers?

I've got a Python application using pytest. For several of my tests, there are API calls to Elasticsearch (using elasticsearch-dsl-py) that slow down my tests that I'd like to:我有一个使用 pytest 的 Python 应用程序。对于我的几个测试,有 API 次调用 Elasticsearch(使用 elasticsearch-dsl-py),这会减慢我想要的测试:

  1. prevent unless a Pytest marker is used.防止除非使用 Pytest 标记。
  2. If a marker is used, I would want that marker to execute some code before the test runs.如果使用标记,我希望该标记在测试运行之前执行一些代码。 Just like how a fixture would work if you used yield .就像使用yield时夹具的工作方式一样。

This is mostly inspired by pytest-django where you have to use the django_db marker in order to make a conn to the database (but they throw an error if you try to connect to the DB, whereas I just don't want a call in the first place, that's all).这主要是受 pytest-django 的启发,你必须使用django_db标记才能连接到数据库(但如果你尝试连接到数据库,它们会抛出错误,而我只是不想调用第一名,仅此而已)。

For example:例如:

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

I think that's a use-case for markers right?我认为这是标记的用例,对吗? Mostly inspired by pytest-django.主要受 pytest-django 启发。

Your initial approach with having a combination of a fixture and a custom marker is the correct one;您最初结合使用固定装置和自定义标记的方法是正确的; in the code below, I took the code from your question and filled in the gaps.在下面的代码中,我从你的问题中提取了代码并填补了空白。

Suppose we have some dummy function to test that uses the official elasticsearch client:假设我们有一些虚拟 function 来测试使用官方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()},
    )

We add two tests, one is not marked with elastic and should operate on fake client, the other one is marked and needs access to real client:我们添加两个测试,一个没有标记为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"

Now let's write the elastic() fixture that will mock the Elasticsearch class depending on whether the elastic marker was set:现在让我们编写elastic() fixture,它将根据是否设置了elastic标记来模拟Elasticsearch class:

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()

Notice the usage of autouse=True : the fixture will be executed on each test invocation, but only do the patching if the test is not marked.请注意autouse=True的用法:fixture 将在每次测试调用时执行,但仅在测试标记时才进行修补。 This presence of the marker is checked via request.node.get_closest_marker("elastic") is None .通过request.node.get_closest_marker("elastic") is None检查标记的存在。 If you run both tests now, test_fake will pass because elastic mocks the Elasticsearch.index() response, while test_real will fail, assuming you don't have a server running on port 9200.如果您现在运行这两个测试, test_fake将通过,因为elastic Elasticsearch.index()响应,而test_real将失败,假设您没有在端口 9200 上运行的服务器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM