тест: как издеваться над дополнительной зависимостью?

#python #mocking #pytest

Вопрос:

В проекте Python я использую асинхронный клиент Elasticsearch, подобный этому:

 pip install elasticsearch[async]
 
 from elasticsearch import AsyncElasticsearch

async def es_search(...):
    es_client = AsyncElasticsearch(url, timeout=30)
    es_response = await es_client.search(...)
    return es_response
 

Это работает, как и ожидалось.

Теперь я хочу написать модульные тесты и поиздеваться AsyncElasticsearch над поведением клиента с помощью pytest «s mock «.

Я попытался написать базовый тест макет, подобный этому (где test_app и mocker вводятся приспособления для тестирования).:

 def test_es_search(test_app, mocker):
    mocker.patch('elasticsearch.AsyncElasticsearch.search', return_value={"data": "foobar"})
    result = es_search()
    assert result == {"data": "foobar"}
 

При запуске моего теста pytest происходит сбой с этой ошибкой:

 thing = <class 'elasticsearch._async.client.AsyncElasticsearch'>
comp = 'cluster', import_path = 'elasticsearch.AsyncElasticsearch.cluster'

    def _dot_lookup(thing, comp, import_path):
        try:
            return getattr(thing, comp)
        except AttributeError:
>           __import__(import_path)
E           ModuleNotFoundError: No module named 'elasticsearch.AsyncElasticsearch'
 

После просмотра установленных путей lib ( elasticsearch[async] ) я обнаружил, что «реальным» путем для этого AsyncElasticsearch класса является « elasticsearch._async.client.AsyncElasticsearch «. Поэтому я обновил свой макет, чтобы использовать этот путь:

 def test_es_search(test_app, mocker):
    mocker.patch('elasticsearch._async.client.AsyncElasticsearch.search', return_value={"data": "foobar"})
    result = es_search()
    assert result == {"data": "foobar"}
 

Но то же самое, pytest не удалось с:

 thing = <class 'elasticsearch._async.client.AsyncElasticsearch'>
comp = 'cluster'
import_path = 'elasticsearch._async.client.AsyncElasticsearch.cluster'

    def _dot_lookup(thing, comp, import_path):
        try:
            return getattr(thing, comp)
        except AttributeError:
>           __import__(import_path)
E           ModuleNotFoundError: No module named 'elasticsearch._async.client.AsyncElasticsearch'
 

Вот мой вопрос: как я могу издеваться над этим классом, который добавляется в мой проект в качестве «дополнительной» зависимости от pip ? Какой путь я должен использовать, mocker.patch() чтобы высмеять это ? Я что-то упускаю ?

Комментарии:

1. имитируйте, куда вы его импортируете вместо этого. поэтому, если вызывается скрипт, который запускает этот импорт foo.py , вы должны просто выполнять mocker.patch("foo.AsyncElasticsearch")