如何对依赖于urllib2的模块进行单元测试?

2024-04-25 22:00:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一段代码,我不知道如何进行单元测试!该模块使用urllib2从外部XML提要(twitter、flickr、youtube等)中提取内容。这里有一些伪代码:

params = (url, urlencode(data),) if data else (url,)
req = Request(*params)
response = urlopen(req)
#check headers, content-length, etc...
#parse the response XML with lxml...

我的第一个想法是pickle响应并加载它进行测试,但显然urllib的响应对象是不可更改的(它引发了一个异常)。

仅仅从响应体保存XML并不理想,因为我的代码也使用头信息。它被设计成作用于响应对象。

当然,在单元测试中依赖外部数据源是一个可怕的想法。

那么我该如何编写一个单元测试呢?


Tags: 模块对象代码url内容datayoutuberesponse
3条回答

urllib2有一个名为build_opener()install_opener()的函数,您应该使用它来模拟urlopen()的行为

import urllib2
from StringIO import StringIO

def mock_response(req):
    if req.get_full_url() == "http://example.com":
        resp = urllib2.addinfourl(StringIO("mock file"), "mock message", req.get_full_url())
        resp.code = 200
        resp.msg = "OK"
        return resp

class MyHTTPHandler(urllib2.HTTPHandler):
    def http_open(self, req):
        print "mock opener"
        return mock_response(req)

my_opener = urllib2.build_opener(MyHTTPHandler)
urllib2.install_opener(my_opener)

response=urllib2.urlopen("http://example.com")
print response.read()
print response.code
print response.msg

最好是编写一个模拟urlopen(可能还有请求),它提供了与urllib2的版本类似的最低要求的接口。然后,您需要让使用它的函数/方法以某种方式接受这个模拟urlopen,并使用urllib2.urlopen否则。

这是相当多的工作,但值得。请记住,python对ducktyping非常友好,所以您只需要提供响应对象属性的一些外观来模拟它。

例如:

class MockResponse(object):
    def __init__(self, resp_data, code=200, msg='OK'):
        self.resp_data = resp_data
        self.code = code
        self.msg = msg
        self.headers = {'content-type': 'text/xml; charset=utf-8'}

    def read(self):
        return self.resp_data

    def getcode(self):
        return self.code

    # Define other members and properties you want

def mock_urlopen(request):
    return MockResponse(r'<xml document>')

当然,其中一些很难模拟,因为例如,我相信普通的“headers”是一个HTTPMessage,它实现了一些有趣的东西,比如不区分大小写的头名称。但是,您可以使用响应数据简单地构造HTTPMessage。

构建一个单独的类或模块,负责与外部feed通信。

使这个类能够成为test double。你使用的是python,所以你在这方面非常出色;如果你使用的是C#,我建议要么使用接口方法,要么使用虚拟方法。

在单元测试中,插入外部feed类的test double。测试代码是否正确地使用了类,假设该类正确地完成了与外部资源通信的工作。让您的测试double返回假数据而不是实时数据;测试数据的各种组合,当然还有urllib2可能抛出的异常。

还有。。。就这样。

您不能有效地自动化依赖于外部源的单元测试,所以最好不要这样做。在通信模块上偶尔运行集成测试,但不要将这些测试作为自动化测试的一部分。

编辑:

我的答案和克拉斯特的答案有什么不同。两者基本上都是正确的,但它们涉及不同的方法。在Crast的方法中,在库本身使用test double。在我的方法中,将库的使用抽象为一个单独的模块,并对该模块进行双重测试。

你用哪种方法完全是主观的,没有“正确”的答案。我更喜欢我的方法,因为它允许我构建更模块化、更灵活的代码,这是我所看重的。但是,在编写额外的代码方面,这是需要付出代价的,而在许多敏捷环境中,这一点可能没有价值。

相关问题 更多 >

    热门问题