I am writing tests for legacy software which has a dependency on CGI scripts on a remote machine.
Mocking the dependency would entail modifying the code under test, which is a thing I cannot do at the moment. For this reason, I decided to make a replica of the remote machine for the code to run against (I have made a copy of the CGI scripts on my development machine and provided a way to set up a local http server within my tests).
Since I don't have much experience with CGI, I have written some "meta-tests" to ensure the local server is working correctly.
Test folder structure is:
root | `- src/ | `-- #code to test... | `-functional_tests/ |-- __init__.py |-- tests.py `-- www `-- cgi-bin |-- # legacy cgi scripts... `-- hello_world.sh #tests.py from contextlib import contextmanager from http.client import HTTPResponse from http.server import CGIHTTPRequestHandler, HTTPServer import threading from unittest import TestCase import urllib.request from urllib.error import URLError # custom handler for my dir structure class _TestHandler(CGIHTTPRequestHandler): def __init__(self, request, client_address, server): super().__init__( request, client_address, server, directory="functional_tests/www" ) self.cgi_directories = ["/cgi-bin"] # utility function invoked by tests which involve cgi @contextmanager def _setup_cgi_http_server(): # runs a stoppable http.server.HTTPServer instance (see https://stackoverflow.com/a/19211760/19831748) def _run_server(httpd: HTTPServer, stop_event: threading.Event): # prevents handle_request() from hanging httpd.timeout = 0.25 print("http server started...") while not stop_event.is_set(): httpd.handle_request() print("...http server stopped") stop_event = threading.Event() with HTTPServer(("", 8000), _TestHandler) as httpd: server_thread = threading.Thread(target=_run_server, args=[httpd, stop_event]) server_thread.start() yield httpd stop_event.set() class TestCGIHTTPServer(TestCase): def test_can_invoke_cgi(self): TEST_CGI_URL = "http://localhost:8000/cgi-bin/hello_world.sh" self.enterContext(_setup_cgi_http_server()) with urllib.request.urlopen(TEST_CGI_URL) as response: assert isinstance(response, HTTPResponse) content = response.read().decode("utf-8") self.assertIn("hello world", content.lower()) def test_server_is_closed_after_exit(self): with _setup_cgi_http_server() as _: pass with self.assertRaises((URLError, ConnectionResetError)): urllib.request.urlopen("http://localhost:8000") #!/bin/bash # hello_world.sh echo "Content-Type: text/plain" echo "" echo "Hello world"
_setup_cgi_http_server()is invoked duringsetUp()\$\endgroup\$