Я пытаюсь создать тест, который выполняет некоторые тесты с 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.

Я не понимаю, почему так происходит. В документации написано, что реактор запускается на пробу и трогать его не следует. Я неправильно использую среду тестирования?

0
cast 30 Дек 2017 в 15:24

2 ответа

Лучший ответ

Вероятно, вам не следует использовать реальный сетевой трафик в вашем наборе тестов. Реальная сеть нестабильна, и полагающиеся на нее тестовые наборы склонны к ошибкам и вызывают разочарование. Вы действительно не хотите, чтобы ваш тестовый прогон завершился неудачно только потому, что systemd-resolved обновился и начал делать что-то дурацкое с некоторым DNS-трафиком, который он заметил, проходящим по вашей системе.

Основная стратегия предотвращения реального сетевого трафика состоит в том, чтобы иметь альтернативную реализацию интерфейса на один уровень ниже того, который вы тестируете - реализация, которая просто не использует реальную сеть. Моя стратегия - имитировать сетевое поведение с помощью простых объектов в памяти. При желании вы можете затем запустить набор тестов для этого более низкого уровня как для реальной реализации, так и для реализации в памяти и убедиться, что обе реализации «одинаковы», по крайней мере, до определенного момента.

Тем не менее, в вашем tearDown есть простая ошибка. Он вызывает stopListening, который возвращает Deferred, но не возвращает сам Deferred. Таким образом, испытание решает, что очистка завершена, когда tearDown возвращается, но может еще не быть. Верните stopListening Deferred, и вы сможете избежать одной нечистой ошибки.

Похожая ошибка есть в test_test. Он не возвращает d, поэтому испытание решает, что тест окончен (успешно), как только метод возвращается. Верните d, и он решит, что тест окончен, когда сработает d (и пройдет тест, только если он сработает с успешным результатом).

0
Jean-Paul Calderone 30 Дек 2017 в 22:01

У меня возникла аналогичная проблема при разрешении 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()

И вам просто нужно отменить вызов после того, как ваш тест закончится, и ваш реактор будет чист.

0
Pedro Ferreira 27 Авг 2019 в 18:10