Ivan Ivanov
Ivan Ivanov

Reputation: 2052

Django: block internet connection for testing purposes

I'd like to make sure my unit tests do not try to connect to Internet, is there a way to raise an exception when they do?

There was a similar question Python: block network connections for testing purposes?, but the solution proposed there blocks all socket connections, including the database ones, which is not acceptable for Django tests.

Upvotes: 4

Views: 2350

Answers (3)

Tom Wojcik
Tom Wojcik

Reputation: 6189

httpretty solves this problem.

Write a custom test runner where you disable only 3rd party API calls, only in your test suite.

# 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)

add this new test runner to your settings

TEST_RUNNER = "common.test_runner.CustomTestRunner"

And from now on all external API calls have to be mocked or httpretty.errors.UnmockedError will be raised.

Alternatively, if your project uses responses library, responses.activate or responses.start should work similarly.

Upvotes: 2

Ivan Ivanov
Ivan Ivanov

Reputation: 2052

Found out a way to do this. You can paste this into your settings.

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

Upvotes: 5

MDB
MDB

Reputation: 2189

Take a look at vcrpy: https://github.com/kevin1024/vcrpy

The first time you run the test it'll make the external request. Every time after that it'll use a fixture that it stored from the first call. The external requests never get made again, which is what you're trying to accomplish.

Usage is dead simple using a decorator on your unit test:

@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

Supports any request made using the following libraries:

  • urllib2
  • urllib3
  • http.client (python3)
  • requests (both 1.x and 2.x versions)
  • httplib2
  • boto
  • Tornado’s AsyncHTTPClient

Upvotes: 2

Related Questions