Skip to content

Elegant way to exclude middleware in unit tests? #582

@fgimian

Description

@fgimian

Description

Is it possible to disable or override middleware in unit tests?

Additional context

I have written some middleware to obtain a database pool connection and release it when the request is done. I'm using asyncpg directly for this as follows:

@app.middleware("http")
async def database_middleware(request: Request, call_next):
    conn = None
    try:
        conn = await database_pool.acquire()

        try:
            await conn.fetch("SELECT 1")
        except ConnectionDoesNotExistError:
            conn = await database_pool.acquire()

        request.state.db = conn
        return await call_next(request)
    except (PostgresConnectionError, OSError) as e:
        logger.error("Unable to connect to the database: %s", e)
        return Response(
            "Unable to connect to the database.", status_code=HTTP_500_INTERNAL_SERVER_ERROR
        )
    except SyntaxOrAccessError as e:
        logger.error("Unable to execute query: %s", e)
        return Response(
            "Unable to execute the required query to obtain data from the database.",
            status_code=HTTP_500_INTERNAL_SERVER_ERROR,
        )
    finally:
        if conn:
            await database_pool.release(conn)

I then have a little get_db dependency injection which is similar to the docs:

def get_db(request: Request):
    return request.state.db

I'd like to mock my database in my unit tests so I can verify that I'm building the correct SQL given the appropriate params.

I've setup a pytest fixture to mock the database as follows:

@pytest.fixture(scope="function")
def db_mock(request):
    def fin():
        del app.dependency_overrides[get_db]

    db_mock = mock.MagicMock()
    app.dependency_overrides[get_db] = lambda: db_mock

    request.addfinalizer(fin)
    return db_mock

This works perfectly when the middleware is disabled. However, when it's enabled, naturally all endpoints fail with a 500 error as the database fails to connect.

After looking a little deeper into starlette codebase and the way middleware works, I couldn't seem to find an elegant way to disable or override middleware in my unit tests.

Any help is greatly appreciated. Also if you see anything wrong with the approach I'm taking, please do let me know.

Huge thanks!
Fotis

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions