Skip to main content

HTTP server for testing environments

Project description

On-demand HTTP server for use in test environments where mocking underlying calls isn’t an option or where it’s necessary to have an actual HTTP server listening on a socket (e.g. testing IPv6 connectivity). Multiple HTTP servers can be run concurrently, and by default the port number is the next available unused port.

Compatibility

Spoof was tested with the following versions of Python (2.6.x and 3.3.x omitted due to SSL compatibility issues):

  • 3.7.1

  • 3.6.7

  • 3.5.4

  • 3.4.7

  • 2.7.15

Installation

Install from source:

python setup.py install

Install from PyPI:

pip install spoof

Example usage

import unittest
import spoof
import thing

class TestThing(unittest.TestCase):
  httpd = None
  httpd6 = None

  @classmethod
  def setUpClass(cls):
    # X509 certificates can be expensive to generate, so it should be done
    # infrequently.  Also, creating a new HTTP server instance with a new
    # port number for each and every test can starve a system of available
    # TCP/IP ports.  Because of this, creating an `HTTPServer` instance
    # should also be done infrequently, unless the port number is static.
    sslContext = spoof.SSLContext.selfSigned()
    cls.httpd = spoof.HTTPServer(sslContext=sslContext)
    cls.httpd.start()
    # IPv6-only, if needed; `HTTPServer` also accepts IPv6 addresses
    cls.httpd6 = spoof.HTTPServer6(sslContext=sslContext)
    cls.httpd6.start()

  @classmethod
  def tearDownClass(cls):
    cls.httpd.stop()
    cls.httpd6.stop()
    cls.httpd = None
    cls.httpd6 = None

  def setUp(self):
    # Calling `reset()` suffices to sanitize the HTTP server environment.
    self.httpd.reset()
    self.httpd.debug = False
    self.thing = thing.Thing(self.httpd.address, self.httpd.port)
    # or
    self.altThing = thing.AltThing(self.httpd.url)

  def tearDown(self):
    self.thing = None
    self.altThing = None

  def test_thingUsingSpoof(self):
    response1 = [200, [('Content-Type', 'application/json')], '{"id": 1111}']
    response2 = [200, [('Content-Type', 'application/json')], '{"id": 2222}']
    self.httpd.queueResponse(response1)
    self.httpd.queueResponse(response2)
    # HTTP debug logging, if needed
    self.httpd.debug = True
    self.thing.requiringTwoJSONresponses()
    lastRequest = self.httpd.requests[-1]
    expectedContent = '{"action": "rename", "old": 1111, "new": 2222}'
    self.assertEqual(expectedContent, lastRequest.content)

Squelching SSL warnings

Some libraries like Requests will complain loudly or refuse to connect to HTTP servers with a self-signed SSL certificate. The preferred way to handle this is to use the verify property in requests.Session to trust the certificate:

import requests
import spoof

cert, key = spoof.SSLContext.createSelfSignedCert()
sslContext = spoof.SSLContext.fromCertChain(cert, key)
httpd = spoof.HTTPServer(sslContext=sslContext)
httpd.queueResponse((200, (), 'OK'))
httpd.start()

# trust self-signed certificate
session = requests.Session()
session.verify = cert

response = session.get(httpd.url + '/uri/path')
print(response.status_code, response.content)
httpd.stop()

If verifying the certificate is not an option, another way to work around this is to monkeypatch the requests library in the testing code. For example:

import requests

certVerify = requests.adapters.HTTPAdapter.cert_verify
def certNoVerify(self, conn, url, verify, cert):
    return certVerify(self, conn, url, False, cert)
requests.adapters.HTTPAdapter.cert_verify = certNoVerify
requests.packages.urllib3.disable_warnings()

Another common case is libraries that leverage ssl directly. One way to work around this is to globally set the default context to unverified. For example:

import ssl

try:
    createUnverifiedHttpsContext = ssl._create_unverified_context
except AttributeError:
    # ignore if ssl context not verified by default
    pass
else:
    ssl._create_default_https_context = createUnverifiedHttpsContext

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spoof-1.3.0.tar.gz (9.5 kB view hashes)

Uploaded Source

Built Distribution

spoof-1.3.0-py2.py3-none-any.whl (10.3 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page