2014-06-09 65 views
1

我几年没有做任何扭曲,并已开始使用客户端HTTP调用的新型代理风格。使用代理已经可以,但测试让我感到困惑(毕竟它是扭曲的)。单元测试twisted.web.client.Agent的没有网络

我已经通过了https://twistedmatrix.com/documents/current/core/howto/trial.html文档以及试用工具和代理本身的API。还有大量的搜索。

我已经去伪造代理,因为我不需要测试它。但是由于处理代理请求的处理和响应的步骤,我的测试代码变得非常糟糕,实现了代理,协议等的嵌套层。我应该在哪里画线,并且有一些我避难的utils找不到?

这里有一个小例子(幼稚实现SUT的):

from twisted.web.client import Agent, readBody 
from twisted.internet import reactor 
import json 

class SystemUnderTest(object): 

    def __init__(self, url): 
     self.url = url 

    def action(self): 
     d = self._makeAgent().request("GET", self.url) 
     d.addCallback(self._cbSuccess) 
     return d 

    def _makeAgent(self): 
     ''' It's own method so can be overridden in tests ''' 
     return Agent(reactor) 

    def _cbSuccess(self, response): 
     d = readBody(response) 
     d.addCallback(self._cbParse) 
     return d 

    def _cbParse(self, data): 
     self.result = json.loads(data) 
     print self.result 

与测试模块:

from twisted.trial import unittest 
from sut import SystemUnderTest 
from twisted.internet import defer 
from twisted.test import proto_helpers 

class Test(unittest.TestCase): 

    def test1(self): 
     s_u_t = ExtendedSystemUnderTest(None) 
     d = s_u_t.action() 
     d.addCallback(self._checks, s_u_t) 
     return d 

    def _checks(self, result, s_u_t): 
     print result 
     self.assertEqual({'one':1}, s_u_t.result) 


class ExtendedSystemUnderTest(SystemUnderTest): 

    def _makeAgent(self): 
     return FakeSuccessfulAgent("{'one':1}") 

## Getting ridiculous below here... 

class FakeReason(object): 
    def check(self, _): 
     return False 
    def __str__(self): 
     return "It's my reason" 

class FakeResponse(object): 
    ''' Implementation of IResponse ''' 
    def __init__(self, content): 
     self.content = content 
     self.prot = proto_helpers.StringTransport() 
     self.code = 200 
     self.phrase = '' 

    def deliverBody(self, prot): 
     prot.makeConnection(self.prot) 
     prot.dataReceived(self.content) 
#  reason = FakeReason() 
#  prot.connectionLost(reason) 

class FakeSuccessfulAgent(object): 
    ''' Implementation of IAgent ''' 
    def __init__(self, response): 
     self.response = response 

    def request(self, method, url): 
     return defer.succeed(FakeResponse(self.response)) 

回答

2

但是测试是混淆了我(它扭曲毕竟)。

搞笑。

class ExtendedSystemUnderTest(SystemUnderTest): 
    def _makeAgent(self): 
     return FakeSuccessfulAgent("{'one':1}") 

我建议你让代理使用正常的参数。这比专用方法如_makeAgent更方便。作文很棒。继承是meh。

class FakeReason(object): 
    ... 

没有理由对此做出假冒。只需使用twisted.python.failure.Failure即可。您不要假冒对象在测试。只是那些如果你不伪造它们而妨碍你的事情。

class FakeResponse(object): 
    ... 

这可能是好的和必要的。

class FakeSuccessfulAgent(object): 
    ... 

这也是很有必要的。尽管如此,你应该使它更像是一个IAgent实现 - 声明它实现了接口,使用zope.interface.verify.verify{Class,Object}来确保你得到实现写入等等(例如request现在有错误的签名)。

实际上有一张将所有这些测试工具添加到Twisted本身的票 - https://twistedmatrix.com/trac/ticket/4024。所以我不认为你真的很困惑,你基本上和项目本身一样。你刚刚受到Twisted尚未为你完成所有这些工作的事实。

另外请注意,而不是:

class Test(unittest.TestCase): 

    def test1(self): 
     s_u_t = ExtendedSystemUnderTest(None) 
     d = s_u_t.action() 
     d.addCallback(self._checks, s_u_t) 
     return d 

你可以写这样的事情,而不是(并且优选):

class Test(unittest.TestCase): 

    def test1(self): 
     s_u_t = ExtendedSystemUnderTest(None) 
     d = s_u_t.action() 
     self._checks(s_u_t, self.successResultOf(d)) 

这是因为你的假执行IAgent是同步的。你知道它是同步的。到request时,它已经返回了Deferred。以这种方式写测试意味着你可以简化你的代码(也就是说,你可以在一定程度上忽略它的异步性 - 因为它不是)并且它避免运行反应器,反应器是从一个Deferred返回一个Deferred试验中的试验方法。

+1

非常感谢让 - 保罗。很高兴听到有这张票,因为它确实觉得它缺少。 请注意,如果这是运行然后它挂起。似乎协议正在等待dataReceived()之后的某个东西(这就是为什么我有注释掉的断开连接)。任何线索,以什么是缺少完成?谢谢 –

+0

即使'connectionLost'调用没有被注释掉,它也会挂起? –

+0

不,在这种情况下,它与我的理由错误。我如何让它成功完成? PS感谢风格的提示,我做了这些变化 –