Django:为测试目的阻止互联网连接

4

我希望确保我的单元测试不会尝试连接互联网,有没有一种方法在它们这样做时引发异常?

曾经有一个类似的问题Python:用于测试目的阻止网络连接?,但是那里提出的解决方案会阻止所有套接字连接,包括数据库连接,这对Django测试是不可接受的。


可能是重复的问题:在执行Django单元测试时如何禁用第三方API? - Brad Beattie
@BradBeattie 绝对不是,那个问题是关于模拟 API 调用的,我只想确保我已经模拟了所有连接到互联网的东西,并且没有漏掉任何东西(否则我希望测试失败)。 - Ivan Ivanov
3个回答

5
发现了一种方法来实现这个。你可以将以下内容粘贴到你的设置中。
if 'test' in sys.argv:

    # Block Internet access during tests
    import urllib2
    import httplib
    import httplib2

    def _raise_http_error(*args, **kwargs):
        raise urllib2.URLError("I told you not to use the Internet!")

    class AngryHandler(urllib2.BaseHandler):
        handler_order = 1

        def default_open(self, req):
            _raise_http_error()

    opener = urllib2.build_opener(AngryHandler)
    urllib2.install_opener(opener)

    _HTTPHandler = urllib2.HTTPHandler
    urllib2.HTTPHandler = AngryHandler

    httplib.HTTPConnection.connect = lambda self: None
    httplib.HTTPSConnection.connect = lambda self: None
    httplib.HTTPConnection.request = _raise_http_error
    httplib.HTTPSConnection.request = _raise_http_error
    httplib2.Http.request = _raise_http_error

1
似乎更容易阻塞套接字,我认为这是所有这些库的上游。这里有一个示例似乎做了类似的事情:https://dev59.com/cWMl5IYBdhLWcg3wMkko#18601897 - mlissner

2
请看vcrpy:https://github.com/kevin1024/vcrpy 第一次运行测试时,它将发出外部请求。之后每次运行测试时,它都会使用从第一次调用中存储的fixture。外部请求不会再次发出,这就是您要实现的目标。
在您的单元测试上使用装饰器非常简单:
@vcr.use_cassette('fixtures/vcr_cassettes/synopsis.yaml')
def test_iana():
    response = urllib2.urlopen('http://www.iana.org/domains/reserved').read()
    assert 'Example domains' in response

支持使用以下库进行的任何请求:

  • urllib2
  • urllib3
  • http.client(python3)
  • requests(1.x和2.x版本均可)
  • httplib2
  • boto
  • Tornado的AsyncHTTPClient

1

httpretty可以解决这个问题。

编写一个自定义测试运行器,在测试套件中仅禁用第三方API调用。

# common/test_runner.py

import httpretty
from django.test.runner import DiscoverRunner


class CustomTestRunner(DiscoverRunner):
    def run_tests(self, *args, **kwargs):
        with httpretty.enabled(allow_net_connect=False):
            return super().run_tests(*args, **kwargs)


将这个新的测试运行器添加到你的设置中。
TEST_RUNNER = "common.test_runner.CustomTestRunner"

从现在开始,所有的外部API调用都必须被模拟,否则将会引发httpretty.errors.UnmockedError异常。或者,如果您的项目使用responses库,responses.activateresponses.start也可起到类似的作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接