Предварительно тестировать задачи с использованием маркеров pytest?

У меня есть приложение Python, использующее pytest. Для нескольких моих тестов есть вызовы API к Elasticsearch (с использованием elasticsearch-dsl-py), которые замедляют мои тесты, которые я хотел бы:

  1. предотвратить, если не используется маркер Pytest.
  2. Если используется маркер, я бы хотел, чтобы этот маркер выполнял некоторый код перед запуском теста. Точно так же, как приспособление будет работать, если вы используете 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")?
hoefling 24.12.2020 11:40

Для издевательства над elasticsearch, проверьте, например. эластичный макет

hoefling 24.12.2020 11:42

Спасибо @hoefling Ах, я думаю, что декоратор usefixtures идеально подходит для второго случая, а не маркеры. Но как предотвратить вызовы ES по умолчанию (например, когда маркеры/фикстуры не применяются)?

acw 24.12.2020 16:18

Думаю, теперь я понимаю, что вы имеете в виду, скоро добавлю ответ.

hoefling 24.12.2020 20:35
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
425
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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(), которая будет имитировать класс Elasticsearch в зависимости от того, был ли установлен маркер elastic:

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: фикстура будет выполняться при каждом вызове теста, но исправление будет выполняться только в том случае, если тест не помечен. Наличие маркера проверяется с помощью request.node.get_closest_marker("elastic") is None. Если вы запустите оба теста сейчас, test_fake пройдет, потому что elastic имитирует ответ Elasticsearch.index(), а test_real завершится ошибкой, если у вас нет сервера, работающего на порту 9200.

Очень доволен этим ответом !!! Я так и не научился правильно использовать макеты и патчи. Я думаю, что это достаточно хороший ответ, чтобы закрепиться, так что принято. Я думаю, меня беспокоит только то, что вы издеваетесь над конкретным случаем возвращения. Я надеялся найти способ заблокировать вызовы, но я думаю, что фиктивная библиотека Elasticsearch, вероятно, подойдет. Благодаря тонну!

acw 25.12.2020 01:17

Да, поправьте меня, если я ошибаюсь - я полагаю, это скорее заметка для себя, - но использование библиотеки Elasticmock, вероятно, означало бы не использование их декоратора, а вместо этого исправление с использованием _get_elasticmock. github.com/vrcmarcos/elasticmock/blob/…

acw 25.12.2020 01:26

@acw вы также можете просто сделать patcher = patch('lib.Elasticsearch', FakeElasticsearch); _get_elasticmock — это просто способ кеширования поддельных клиентов для повторного использования. Я согласен, что публичный API elasticmock довольно лаконичен, но может предложить больше возможностей для насмешек.

hoefling 25.12.2020 14:06

Другие вопросы по теме