Start a server in a subprocess during a pytest session

I wanted to start an actual server process, run it for the duration of my pytest session and shut it down at the end.

Here's the recipe I came up with. This fixture lives in

import pytest
import sqlite_utils
import subprocess

def ds_server(tmp_path_factory):
    db_directory = tmp_path_factory.mktemp("dbs")
    db_path = db_directory / "test.db"
    db = sqlite_utils.Database(db_path)
    ds_proc = subprocess.Popen(
    # Give the server time to start
    # Check it started successfully
    assert not ds_proc.poll(),"utf-8")
    yield ds_proc
    # Shut it down at the end of the pytest session

A test looks like this:

import httpx

def test_server_starts(ds_server):
    response = httpx.get("")
    assert response.status_code == 200

Alternative recipe for serving static files

While adding tests to Datasette Lite I found myself needing to run a localhost server that served static files directly.

I completely forgot about this TIL, and instead took inspiration from pytest-simplehttpserver - coming up with this pattern:

from subprocess import Popen, PIPE
import pathlib
import pytest
import time
from http.client import HTTPConnection

root = pathlib.Path(__file__).parent.parent.absolute()

def static_server():
    process = Popen(
        ["python", "-m", "http.server", "8123", "--directory", root], stdout=PIPE
    retries = 5
    while retries > 0:
        conn = HTTPConnection("localhost:8123")
            conn.request("HEAD", "/")
            response = conn.getresponse()
            if response is not None:
                yield process
        except ConnectionRefusedError:
            retries -= 1

    if not retries:
        raise RuntimeError("Failed to start http server")

Again, including static_server as a fixture is enough to ensure requests to http://localhost:8123/ will be served by that temporary server.

I like how this version polls for a successful HEAD request (a trick inspired by pytest-simplehttpserver) rather than just sleeping.

Created 2020-08-31T19:06:36-07:00, updated 2022-07-22T18:20:37-07:00 · History · Edit