Записывать ввод и вывод в тестовой среде, которая использует 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"""
    ...
Другие вопросы по тегам