【问题标题】:How can I write tests for code using twisted.web.client.Agent and its subclasses?如何使用 twisted.web.client.Agent 及其子类编写代码测试?
【发布时间】:2013-08-25 12:49:31
【问题描述】:

我阅读了关于测试驱动开发的official tutorial,但它对我的情况并没有太大帮助。我编写了一个小型库,广泛使用了twisted.web.client.Agent 及其子类(例如BrowserLikeRedirectAgent),但我一直在努力使教程的代码适应我自己的测试用例。

我查看了twisted.web.test.test_web,但我不明白如何将所有部分组合在一起。例如,按照官方教程,我仍然不知道如何从Agent 中获取Protocol 对象

谁能告诉我如何为一些依赖代理到GETPOST 数据的代码编写一个简单的测试?欢迎任何其他详细信息或建议...

非常感谢!

【问题讨论】:

  • 我在下面发布了一个答案,但我的一般建议是充分熟悉 Deferreds 和 @inlineCallbacks 以及一般的非阻塞/异步编程原则和概念。此外,研究 gevent 并将其与 Twisted 及其@inlineCallbacks 进行比较可能会提供对该领域的进一步了解(以及为什么不是 Node.js)。

标签: python unit-testing twisted twisted.web


【解决方案1】:

如何使用@inlineCallbacks 让生活更简单(即代码更易读)。

事实上,我什至建议不要直接使用Deferreds,除非对于性能或特定用例绝对必要,而是始终坚持使用@inlineCallbacks——这样你'将使您的代码看起来像普通代码,同时受益于非阻塞行为:

from twisted.internet import reactor
from twisted.web.client import Agent
from twisted.internet.defer import inlineCallbacks
from twisted.trial import unittest
from twisted.web.http_headers import Headers
from twisted.internet.error import DNSLookupError


class SomeTestCase(unittest.TestCase):
    @inlineCallbacks
    def test_smth(self):
        ag = Agent(reactor)
        response = yield ag.request('GET', 'http://example.com/', Headers({'User-Agent': ['Twisted Web Client Example']}), None)
        self.assertEquals(response.code, 200)

    @inlineCallbacks
    def test_exception(self):
        ag = Agent(reactor)
        try:
            yield ag.request('GET', 'http://exampleeee.com/', Headers({'User-Agent': ['Twisted Web Client Example']}), None)
        except DNSLookupError:
            pass
        else:
            self.fail()

试验应该处理其余的事情(即等待从测试函数返回的Deferreds(@inlineCallbacks-包装的可调用对象也“神奇地”返回Deferred——我强烈建议阅读更多关于@inlineCallbacks如果您还不熟悉它)。

附:还有一个用于鼻子测试的 Twisted “插件”,它使您能够从测试函数中返回 Deferreds 并让鼻子等到它们被触发后再退出:http://nose.readthedocs.org/en/latest/api/twistedtools.html

【讨论】:

  • 我接受您的回答,但如果您(或其他人)能够提供不涉及任何实际网络流量的解决方案,我将非常感激。不过谢谢!内联回调使这更易于管理。
  • 我愿意,但首先您需要帮助我理解:您想编写涉及twisted.w.c.Agent 的测试,但想要一个“不涉及任何网络流量”的解决方案?你到底是什么意思?模拟网络并仅测试“纯”逻辑?
  • 是的,这正是我的意思——理想情况下,我希望通过 StringTransport 对象之类的方式发送网络流量。
  • @blz 我面临着模拟网络流量的类似情况。关于你如何解决它的任何提示?
【解决方案2】:

这类似于 mike 所说的,但尝试测试响应处理。还有其他方法可以做到这一点,但我喜欢这种方式。我也同意测试包装代理的东西并没有太大帮助,并且在协议中测试您的协议/保持逻辑可能会更好,但有时您只想添加一些绿色勾号。

class MockResponse(object):
    def __init__(self, response_string):
        self.response_string = response_string

    def deliverBody(self, protocol):
        protocol.dataReceived(self.response_string)
        protocol.connectionLost(None)


class MockAgentDeliverStuff(Agent):

    def request(self, method, uri, headers=None, bodyProducer=None):
        d = Deferred()
        reactor.callLater(0, d.callback, MockResponse(response_body))
        return d

class MyWrapperTestCase(unittest.TestCase):

    def setUp:(self):
        agent = MockAgentDeliverStuff(reactor)
        self.wrapper_object = MyWrapper(agent)

    @inlineCallbacks
    def test_something(self):
        response_object = yield self.wrapper_object("example.com")
        self.assertEqual(response_object, expected_object)

【讨论】:

    【解决方案3】:

    这个怎么样?对以下内容进行试用。基本上,您只是在嘲笑 Agent 并假装它像宣传的那样,并使用 FakeAgent (在这种情况下)使所有请求失败。如果你真的想将数据注入传输,我猜这需要“更多的工作”。但是你真的在测试你的代码吗?还是代理的?

    from twisted.web import client
    from twisted.internet import reactor, defer
    
    class BidnessLogik(object):
        def __init__(self, agent):
            self.agent = agent
            self.money = None
    
        def make_moneee_quik(self):
            d = self.agent.request('GET', 'http://no.traffic.plz')
            d.addCallback(self.made_the_money).addErrback(self.no_dice)
            return d
    
        def made_the_money(self, *args):
            ##print "Moneeyyyy!"
            self.money = True
            return 'money'
    
        def no_dice(self, fail):
            ##print "Better luck next time!!"
            self.money = False
            return 'no dice'
    
    class FailingAgent(client.Agent):
        expected_uri = 'http://no.traffic.plz'
        expected_method = 'GET'
        reasons = ['No Reason']
        test = None
    
        def request(self, method, uri, **kw):
            if self.test:
                self.test.assertEqual(self.expected_uri, uri)
                self.test.assertEqual(self.expected_method, method)
                self.test.assertEqual([], kw.keys())
            return defer.fail(client.ResponseFailed(reasons=self.reasons,
                                                    response=None))
    
    class TestRequest(unittest.TestCase):
        def setUp(self):
            self.agent = FailingAgent(reactor)
            self.agent.test = self
    
        @defer.inlineCallbacks
        def test_foo(self):
            bid = BidnessLogik(self.agent)
            resp = yield bid.make_moneee_quik()
            self.assertEqual(resp, 'no dice')
            self.assertEqual(False, bid.money)
    

    【讨论】:

      猜你喜欢
      • 2013-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-24
      相关资源
      最近更新 更多