Как смоделировать объект hashed_singleton в Python юнит-тестах? - PullRequest
0 голосов
/ 17 января 2020

Моя среда состоит из Python 3.7.5 и притока-клиента библиотеки версии 1.9.0. Я хочу смоделировать метод select_where объекта InfluxDB, но в моем коде (показанном ниже) это приводит к повышению AttributeError, например

AttributeError: <class 'influx.InfluxDB'> does not have the attribute 'select_where'

и

AttributeError: 'InfluxDB' object attribute 'select_where' is read-only

Минимальный код для воспроизведения

influx_mock
├── db_connection
│   ├── __init__.py
│   └── __main__.py
└── tests
    └── test_db_connection.py

с содержимым файлов:

# db_connection/__main__.py

from influx import InfluxDB


def get_data(influx_url):
    client = InfluxDB(influx_url)
    return client.select_where(
        'database', 
        'measurement',
        fields='foo',
        where='bar > 0'
    )

if __name__ == "__main__":
    influx_url = 'http://127.0.0.1:8086'
    data = get_data(influx_url)
    # ...process data
# db_connection/__init__.py

from .__main__ import get_data
# tests/test_db_connection

import unittest
from unittest.mock import patch

from influx import InfluxDB

from db_connection import get_data


class DbConnection(unittest.TestCase):

    def setUp(self):
        self.influx_url = 'http://127.0.0.1:8086'
        self.mock_data = { 'mock data' }

    def test_patching_InfluxDB(self):
        with patch.object(InfluxDB, 'select_where', return_value=self.mock_data):
            data = get_data(self.influx_url)
            self.assertEqual(self.mock_data, data)

    def test_patching_client(self):
        client = InfluxDB(self.influx_url)
        with patch.object(client, 'select_where', return_value=self.mock_data):
            data = get_data(self.influx_url)
            self.assertEqual(self.mock_data, data)

Запуск юниттестов с python -m unittest discover -s tests/ приводит к

EE
======================================================================
ERROR: test_patching_InfluxDB (test_db_connection.DbConnection)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<redacted>/influx_mock/tests/test_db_connection.py", line 17, in test_patching_InfluxDB
    with patch.object(InfluxDB, 'select_where', return_value=self.mock_data):
  File "<redacted>/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py", line 1319, in __enter__
    original, local = self.get_original()
  File "<redacted>/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py", line 1293, in get_original
    "%s does not have the attribute %r" % (target, name)
AttributeError: <class 'influx.InfluxDB'> does not have the attribute 'select_where'

======================================================================
ERROR: test_patching_client (test_db_connection.DbConnection)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<redacted>/influx_mock/tests/test_db_connection.py", line 24, in test_patching_client
    with patch.object(client, 'select_where', return_value=self.mock_data):
  File "<redacted>/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py", line 1410, in __enter__
    setattr(self.target, self.attribute, new_attr)
AttributeError: 'InfluxDB' object attribute 'select_where' is read-only

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (errors=2)

Моя отладка проблемы (если актуально для ответа)

Исправление объекта InfluxDB напрямую (test_patching_InfluxDB) вызывает AttributeError с жалобой на то, что не ' может иметь атрибут select_where. Однако , глядя на исходный код , указывает, что InfluxDB действительно имеет метод класса с именем select_where. Что мешает, так это то, что класс украшен @pytool.lang.hashed_singleton, см. Фрагмент документации:

Оборачивает класс, чтобы создать его хешированную одноэлементную версию. Хешированный синглтон подобен синглтону в том, что для каждой сигнатуры вызова будет только один экземпляр класса.

Синглтон сохраняется как слабая ссылка, поэтому, если ваша программа перестает ссылаться на хешированный синглтон, вы можете получить новый экземпляр, если интерпретатор Python собрал мусор вашего исходного экземпляра.

- https://pytool.readthedocs.io/en/latest/pytool.html#pytool .lang.hashed_singleton

После этого я попробовал второй способ исправления объекта (test_patching_client), думая, что если бы я создал экземпляр InfluxDB с теми же параметрами в тестах, что и в исходном коде, экземпляры были бы одним и тем же объектом, что позволило бы мне смоделировать метод в тестах и ​​перезаписать его в обоих местах. Но это привело к ошибке атрибута только для чтения, так что теперь у меня нет идей.

...