【问题标题】:How to test twisted web resource with trial?如何通过试用版测试扭曲的网络资源?
【发布时间】:2011-07-09 19:32:22
【问题描述】:

我正在开发一个 twisted.web 服务器 - 它包含一些资源,除了渲染内容之外,还使用 ​​adbapi 获取一些数据并将一些数据写入 postgresql 数据库。我试图弄清楚如何编写一个试验单元测试,它可以在不使用 net 的情况下测试资源渲染(换句话说:这将初始化一个资源,产生一个虚拟请求等)。

让我们假设 View 资源是一个简单的叶子,它在 render_GET 中返回 NOT_DONE_YET 并修改 adbapi 以生成简单的文本作为结果。现在,我已经编写了这段无用的代码,但我想不出如何让它真正初始化资源并产生一些合理的响应:

from twisted.trial import unittest
from myserv.views import View
from twisted.web.test.test_web import DummyRequest

class ExistingView(unittest.TestCase):
    def test_rendering(self):
        slug = "hello_world"
        view = View(slug)
        request = DummyRequest([''])
        output = view.render_GET(request)
        self.assertEqual(request.responseCode, 200)

输出是... 1. 我也尝试过这样的方法: output = request.render(view) 但相同的输出 = 1。为什么?我会非常感谢一些示例如何编写这样的单元测试!

【问题讨论】:

    标签: python twisted trial


    【解决方案1】:

    这是一个函数,它将呈现请求并将结果转换为在呈现完成时触发的 Deferred:

    def _render(resource, request):
        result = resource.render(request)
        if isinstance(result, str):
            request.write(result)
            request.finish()
            return succeed(None)
        elif result is server.NOT_DONE_YET:
            if request.finished:
                return succeed(None)
            else:
                return request.notifyFinish()
        else:
            raise ValueError("Unexpected return value: %r" % (result,))
    

    它实际上用在 Twisted Web 的测试套件中,但它是私有的,因为它本身没有单元测试。 ;)

    你可以用它来写一个这样的测试:

    def test_rendering(self):
        slug = "hello_world"
        view = View(slug)
        request = DummyRequest([''])
        d = _render(view, request)
        def rendered(ignored):
            self.assertEquals(request.responseCode, 200)
            self.assertEquals("".join(request.written), "...")
            ...
        d.addCallback(rendered)
        return d
    

    【讨论】:

    • 谢谢!它似乎有效,但扭曲永远不会返回结果 - 我已经检查过 print,它在 request.notifyFinish() 之后等待。我测试过的 slug 在访问时效果很好。可能出了什么问题?
    • 它停在 'request.setHeader' 和 request.setHost - 很可能它们没有在 DummyRequest(或其中之一)中实现。
    • 是的,它失败了:setHeader 和 getCookie - 我认为继承 DummyRequest 并创建 cookie 和 setHeader 功能将解决这个问题:)
    • 有没有办法让在 request.finish() 之后触发的 deferred 在这个测试环境中也被触发?它将统计信息添加到 db :]
    • 我不确定你的意思。任何使用 request.notifyFinish 的东西都应该在这个设置中正常工作。你的意思是那种“在 request.finish() 之后触发的延迟”吗?
    【解决方案2】:

    这是一个 DummierRequest 类,几乎可以解决我的所有问题。唯一剩下的就是它没有设置任何响应代码!为什么?

    from twisted.web.test.test_web import DummyRequest
    from twisted.web import server
    from twisted.internet.defer import succeed
    from twisted.internet import interfaces, reactor, protocol, address
    from twisted.web.http_headers import _DictHeaders, Headers
    
    class DummierRequest(DummyRequest):
        def __init__(self, postpath, session=None):
            DummyRequest.__init__(self, postpath, session)
            self.notifications = []
            self.received_cookies = {}
            self.requestHeaders = Headers()
            self.responseHeaders = Headers()
            self.cookies = [] # outgoing cookies
    
        def setHost(self, host, port, ssl=0):
            self._forceSSL = ssl
            self.requestHeaders.setRawHeaders("host", [host])
            self.host = address.IPv4Address("TCP", host, port)
    
        def addCookie(self, k, v, expires=None, domain=None, path=None, max_age=None, comment=None, secure=None):
            """
            Set an outgoing HTTP cookie.
    
            In general, you should consider using sessions instead of cookies, see
            L{twisted.web.server.Request.getSession} and the
            L{twisted.web.server.Session} class for details.
            """
            cookie = '%s=%s' % (k, v)
            if expires is not None:
                cookie = cookie +"; Expires=%s" % expires
            if domain is not None:
                cookie = cookie +"; Domain=%s" % domain
            if path is not None:
                cookie = cookie +"; Path=%s" % path
            if max_age is not None:
                cookie = cookie +"; Max-Age=%s" % max_age
            if comment is not None:
                cookie = cookie +"; Comment=%s" % comment
            if secure:
                cookie = cookie +"; Secure"
            self.cookies.append(cookie)
    
        def getCookie(self, key):
            """
            Get a cookie that was sent from the network.
            """
            return self.received_cookies.get(key)
    
        def getClientIP(self):
            """
            Return the IPv4 address of the client which made this request, if there
            is one, otherwise C{None}.
            """
            return "192.168.1.199"
    

    【讨论】:

    • 它实际上设置了 404。我是否可以在我的 Resource 类中手动设置代码 200?
    • request.setResponseCode() 通常在ResourceSubclass.render().processingFailed() 方法中调用。
    • 在上面的_render()函数中我们调用request.render()。可能是由使用 NOT_DONE_YET & deferreds 的请求引起的?
    猜你喜欢
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 2011-09-26
    • 2014-08-04
    • 2012-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多