Я пытаюсь создать тест, который выполняет некоторые тесты с DNS-запросами. Я попытался создать минимальный тест, который запускает прослушивающий DNS-сервер и использует скрученный преобразователь для запроса этого сервера:
from twisted.trial import unittest
from twisted.internet import reactor, defer
from twisted.names import client, dns, error, server
class Tester(unittest.TestCase):
def setUp(self):
self.resolver = client.Resolver(resolv='/etc/resolv.conf')
self.resolver = client.Resolver(servers=[('127.0.0.1', 1025)])
self.factory = server.DNSServerFactory(clients=[self.resolver])
self.protocol = dns.DNSDatagramProtocol(controller=self.factory)
self.port = reactor.listenUDP(1025, self.protocol)
def tearDown(self):
self.port.stopListening()
def test_test(self):
def callback(ignore):
print("Received callback!")
res = client.createResolver(servers=[('127.0.0.1', 1025)], resolvconf='/dev/null', hosts='/dev/null')
d = res.lookupAddress('foobar.com')
d.addCallback(callback)
Выполнение этого теста приводит к следующей ошибке:
[ERROR]
Traceback (most recent call last):
Failure: twisted.trial.util.DirtyReactorAggregateError: Reactor was unclean.
DelayedCalls: (set twisted.internet.base.DelayedCall.debug = True to debug)
<DelayedCall 0x7f44c69042e8 [0.9992678165435791s] called=0 cancelled=0
DNSMixin._clearFailed(<Deferred at 0x7f44c6904358>, 28457)>
<DelayedCall 0x7f44c68f3e10 [59.99872899055481s] called=0 cancelled=0 Resolver.maybeParseConfig()>
test.Tester.test_test
==================================================================
[ERROR]
Traceback (most recent call last):
Failure: twisted.trial.util.DirtyReactorAggregateError: Reactor was
unclean.
Selectables:
<<class 'twisted.names.dns.DNSDatagramProtocol'> on 34529>
test.Tester.test_test
-------------------------------------------------------------------------------
Ran 1 tests in 0.003s
Таким образом, кажется, что реактор не очищен от сообщения, которое отправлено преобразователем в test_test.
Я не понимаю, почему так происходит. В документации написано, что реактор запускается на пробу и трогать его не следует. Я неправильно использую среду тестирования?
2 ответа
Вероятно, вам не следует использовать реальный сетевой трафик в вашем наборе тестов. Реальная сеть нестабильна, и полагающиеся на нее тестовые наборы склонны к ошибкам и вызывают разочарование. Вы действительно не хотите, чтобы ваш тестовый прогон завершился неудачно только потому, что systemd-resolved обновился и начал делать что-то дурацкое с некоторым DNS-трафиком, который он заметил, проходящим по вашей системе.
Основная стратегия предотвращения реального сетевого трафика состоит в том, чтобы иметь альтернативную реализацию интерфейса на один уровень ниже того, который вы тестируете - реализация, которая просто не использует реальную сеть. Моя стратегия - имитировать сетевое поведение с помощью простых объектов в памяти. При желании вы можете затем запустить набор тестов для этого более низкого уровня как для реальной реализации, так и для реализации в памяти и убедиться, что обе реализации «одинаковы», по крайней мере, до определенного момента.
Тем не менее, в вашем tearDown
есть простая ошибка. Он вызывает stopListening
, который возвращает Deferred
, но не возвращает сам Deferred
. Таким образом, испытание решает, что очистка завершена, когда tearDown
возвращается, но может еще не быть. Верните stopListening
Deferred
, и вы сможете избежать одной нечистой ошибки.
Похожая ошибка есть в test_test
. Он не возвращает d
, поэтому испытание решает, что тест окончен (успешно), как только метод возвращается. Верните d
, и он решит, что тест окончен, когда сработает d
(и пройдет тест, только если он сработает с успешным результатом).
У меня возникла аналогичная проблема при разрешении DNS-запроса с помощью twisted.
Моя проблема заключалась в том, что twisted запускает цикл каждые 60 секунд для проверки файла resolv.conf.
Это делается в методе maybeParseConfig
в эта строка
self._parseCall = self._reactor.callLater(self._resolvReadInterval, self.maybeParseConfig)
Этот self._parseCall
содержит отложенный вызов, который делает ваш реактор нечистым.
Итак, хотя я согласен с ответом @Jan, я не хотел прямо сейчас создавать свой собственный поддельный преобразователь DNS и придумал обходной путь
Для начала вам нужно получить скрученный резольвер:
import twisted.names.client
resolver_chain = twisted.names.client.getResolver()
Это возвращает twisted.names.resolve.ResolverChain
, который, в моем случае, имел список с 3 преобразователями в атрибуте resolvers
. Тот, который нужно очистить, - это тот из twisted.names.client.Resolver
, который в моем случае был третьим.
resolver = resolver_chain.resolvers[2]
resolver._parseCall.cancel()
И вам просто нужно отменить вызов после того, как ваш тест закончится, и ваш реактор будет чист.
Похожие вопросы
Новые вопросы
dns
Вопросы DNS должны быть связаны с программированием. Используйте этот тег для программирования вопросов, связанных с написанием кода, взаимодействующего с Системой доменных имен (DNS); например, написание кода, который использует gethostbyname ()