Скрученный тест: реактор был нечистым - PullRequest
0 голосов
/ 24 августа 2018

Я работаю над реализацией потокового протокола и в настоящее время пытаюсь настроить модульные тесты, прежде чем использовать новую реализацию в коде приложения.

При создании простой версии теста я столкнулся с проблемой оставления реактора "нечистым". Я обратился к этому, реализовав следующую логику демонтажа:

def tearDown(self):
    def loseConnection(connection):
        connection.transport.loseConnection()

    # Closing all open connections
    for _, connection in self.tcp_transport._connections.iteritems():
        if isinstance(connection, Protocol):
            connection.transport.loseConnection()
        elif isinstance(connection, Deferred):
            connection.addCallback(loseConnection)

Простой тестовый пример, который работает

def test_send_to_new_connection(self):
    # Given
    peerAddr = ('10.22.22.190', 5060)

    # If
    self.tcp_transport.send_to('test', peerAddr)

    # Then
    assert peerAddr in self.tcp_transport._connections
    assert True == isinstance(self.tcp_transport._connections[peerAddr], Deferred)

При расширении тестового примера путем ручного разрешения отложенного значения с помощью «фиктивного» значения (на самом деле не фиктивного, просто используя proto_helpers для создания поддельного протокола), тестовый пример начинает давать сбой с тем же сообщением Reactor was unclean, однако в этом случае кажется, что существующая логика tearDown не способна позаботиться об очистке.

Расширенный тестовый набор, который не работает

def test_send_to_new_connection(self):
    # Given
    peerAddr = ('10.22.22.190', 5060)

    # If
    self.tcp_transport.send_to('test', peerAddr)

    # Then
    assert peerAddr in self.tcp_transport._connections
    assert True == isinstance(self.tcp_transport._connections[peerAddr], Deferred)

    connection = _string_transport_connection(self.hostAddr, peerAddr, None, self.tcp_transport.connectionMade)

    def assert_cache_updated_on_connection(connection):
        print('--------- SUCCESS ----------')
        peer = connection.transport.getPeer()
        peerAddr = (peer.host, peer.port)

        assert peerAddr in self.tcp_transport._connections
        assert True == isinstance(self.tcp_transport._connections[peerAddr], Protocol)

    def assert_fail(fail):
        print('--------- FAIL ----------')

    self.tcp_transport._connections[peerAddr].addCallback(assert_cache_updated_on_connection)
    self.tcp_transport._connections[peerAddr].addErrback(assert_fail)
    # Forcing deferred to fire with mock connection
    self.tcp_transport._connections[peerAddr].callback(connection)

Подробное сообщение об ошибке

DirtyReactorAggregateError: Reactor was unclean.
Selectables:
<<class 'twisted.internet.tcp.Client'> to ('10.22.22.190', 5060) at 361e650>

Из этого сообщения я предполагаю, что где-то висит клиент. Мое единственное подозрение лежит на TCP4ClientEndpoint, который создается тестируемым кодом, но никогда не удается подключиться (из-за предоставленного поддельного IP-адреса). Я был бы счастлив очистить его, но проблема в том, что я не знаю, как получить доступ к нему из отложенного:

def _createConnection(self, address):
    endpoint = TCP4ClientEndpoint(reactor, address[0], address[1])
    protocol = TCPProtocol(self.dataReceived, self.cacheConnection)
    d = connectProtocol(endpoint, protocol)
    ...
    return d

Итак, у меня есть доступ к d, поскольку я могу разрешить его с помощью фиктивного соединения, но как я могу получить доступ к протоколу, который был изначально создан для закрытия этого соединения?

...