2014-06-09 63 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感謝風格的提示,我做了這些變化 –