Я работаю над реализацией потокового протокола и в настоящее время пытаюсь настроить модульные тесты, прежде чем использовать новую реализацию в коде приложения.
При создании простой версии теста я столкнулся с проблемой оставления реактора "нечистым". Я обратился к этому, реализовав следующую логику демонтажа:
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
, поскольку я могу разрешить его с помощью фиктивного соединения, но как я могу получить доступ к протоколу, который был изначально создан для закрытия этого соединения?