Записывать ввод и вывод в тестовой среде, которая использует Python, Pyro и pytest
Я думаю, что было бы лучше, если бы я сначала объяснил, что я хочу сделать: в настоящее время я разрабатываю среду тестирования с использованием Python, pytest и Pyro4. Он предназначен для тестирования программного обеспечения, работающего на аппаратных прототипах. Разработчик / тестировщик выполняет тестовый скрипт на своем ПК локально, используя pytest. В тестовом сценарии действия выполняются с использованием прокси-объектов Pyro4, которые подключаются к серверу Pyro4 (демону), работающему на Raspberry Pi. Реализация на Raspberry Pi адаптирует различные датчики и реле, подключенные к тестируемому устройству, представленные в виде объектов, которые затем передаются демону Pyro4.
Вот упрощенный пример того, как может выглядеть тестовый скрипт. Что он делает: нажмите кнопку x и измерьте, светится ли светодиод y.
@pytest.fixture(scope="module")
def test_components():
# initialize a dict of pyro proxy objects that represent a hardware component, controlled by the Raspberry Pi
a_dict = {}
a_dict['button_x'] = Pyro4.Proxy("PYRO:button_x@192.168.1.2:1234")
a_dict['status_led'] = Pyro4.Proxy("PYRO:status_led@192.168.1.2:1234")
return a_dict
def test_functionality_button_led(test_components):
test_components['button_x'].set_for(0.5) # presses button x / enables relay x for 0.5 seconds
time.sleep(1)
assert test_compontents['status_led'].get_level() > 0 # assert that led is on
Теперь к моему первоначальному вопросу: после каждого запуска теста я хочу иметь файл журнала, который показывает вызовы функций, их входные значения и их возвращаемые значения. Так это должно выглядеть примерно так:
0.1: test_components('button_x').set_for(0.5) [None]
0.2: time.sleep(1) [None]
1.2: assert test_compontents('status_led').get_level() [1] > 0
Я имел в виду два варианта, но не смог архивировать это:
Использование Pytest
Я надеялся найти какой-нибудь переключатель или плагин (например, pytest-logging), который бы помог мне сделать это, но я просто не мог найти что-нибудь, чтобы это заархивировать.
Использование Pyro
Это где я немного пошевелился. Я надеялся, что смогу создать подкласс Pyro4 Proxy (клиент) для этого. Я подкласс Pyro4.Proxy и переопределил __getattr__
метод, который вызывается для получения методов от удаленного объекта во время выполнения. Используя атрибут name, теперь я могу по крайней мере определить, когда и какой метод вызывается, но я все еще не могу получить возвращаемые и входные значения.
class ImprovedProxy(Pyro4.Proxy):
def __getattr__(self, name):
print(name)
return = Pyro4.Proxy.__getattr__(self, name)
Чтобы получить входные и выходные значения, мне нужно было бы создать подкласс и заменить _RemoteMethod
класс, который возвращается Pyro4.Proxy.__getattr__(self, name)
но я даже не мог запустить оригинальный код из Pyro4.Proxy.__getattr__(self, name)
в моем ImprovedProxy.__getattr__(self, name)
без множества ошибок посмотрите сами:
class ImprovedProxy(Pyro4.Proxy):
def __getattr__(self, name):
if name in Pyro4.Proxy.__pyroAttributes:
# allows it to be safely pickled
raise AttributeError(name)
if config.METADATA:
# get metadata if it's not there yet
if not self._pyroMethods and not self._pyroAttrs:
self._pyroGetMetadata()
if name in self._pyroAttrs:
return self._pyroInvoke("__getattr__", (name,), None)
if config.METADATA and name not in self._pyroMethods:
# client side check if the requested attr actually exists
raise AttributeError("remote object '%s' has no exposed attribute or method '%s'" % (self._pyroUri, name))
if self.__async:
return _AsyncRemoteMethod(self, name, self._pyroMaxRetries)
return _RemoteMethod(self._pyroInvoke, name, self._pyroMaxRetries)
Трассировка была:
File "C:\...\client.py", line 38, in __getattr__
if name in Pyro4.Proxy.__pyroAttributes:
AttributeError: type object 'Proxy' has no attribute '_ImprovedProxy__pyroAttributes'
Не уверен, почему здесь добавлено имя моего подкласса, и после того, как я немного поигрался с кодом, я не уверен, что это правильный путь.
Есть ли лучший способ решить эту проблему? В качестве последнего средства я мог бы регистрировать действия на сервере и отправлять их клиенту, но я хотел бы избежать этого, одной из причин является то, что у меня работает несколько таких тестовых адаптеров, которые затем требуют синхронизации по точному времени.
Я надеюсь, что смогу объяснить это достаточно хорошо. Ждем ваших ответов.
1 ответ
Причина, по которой вы видите_ImprovedProxy__pyroAttributes
'в вашей трассировке, потому что ваш код пытается получить доступ к закрытому атрибуту на базовом прокси. Первая строка в вашем __getattr__
метод. Python делает свое стандартное искажение имен, как и для всех атрибутов, начинающихся с двойного подчеркивания, чтобы сделать его "приватным" атрибутом. Вы не должны иметь доступ к этому атрибуту.
Теперь фактическое решение вашей проблемы, однако, простое, я думаю: переопределить прокси _pyroInvoke
метод вместо. Это то, что в конечном итоге вызывается, и это логика, которая делает фактический вызов удаленного метода. Я думаю, что он предоставляет все аргументы, которые вам нужны:
def _pyroInvoke(self, methodname, vargs, kwargs, flags=0, objectId=None):
"""perform the remote method call communication"""
...