question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Documented testing approach does not work for SQLite in-memory instances

See original GitHub issue

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn’t find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google “How to X in FastAPI” and didn’t find any information.
  • I already read and followed all the tutorial in the docs and didn’t find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

# ----------------------------------------------------------------
# app.py

from __future__ import annotations

from fastapi import Depends, FastAPI
from sqlalchemy import Column, Integer, create_engine
from sqlalchemy.orm import Session, declarative_base, sessionmaker  # type: ignore

Base = declarative_base()


class DbExample(Base):  # type: ignore
    __tablename__ = "app_examples"

    example_id = Column(Integer, primary_key=True)


def create_db(url: str):
    engine = create_engine(
        url,
        connect_args={"check_same_thread": False},
        native_datetime=True,
    )
    SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
    Base.metadata.create_all(bind=engine)

    return SessionLocal


SessionLocal = create_db("sqlite:///./real.db")
app = FastAPI()


def get_db():
    db = SessionLocal()

    try:
        yield db
    finally:
        db.close()


@app.get("/")
def summary(*, db: Session = Depends(get_db)):
    res = db.execute("SELECT COUNT(*) FROM app_examples").scalars()

    return {"result": next(res)}
# ----------------------------------------------------------------
# test_app.py

from __future__ import annotations

from fastapi.testclient import TestClient

from app import app, create_db, get_db

TEST_DB_URL = "sqlite://"


def test_root_with_context_manager() -> None:
    from contextlib import contextmanager

    @contextmanager
    def _session():
        TestingSessionLocal = create_db(TEST_DB_URL)
        saved_get_db = get_db in app.dependency_overrides
        old_get_db = app.dependency_overrides.get(get_db)

        def override_get_db():
            try:
                db = TestingSessionLocal()
                yield db
            finally:
                db.close()

        app.dependency_overrides[get_db] = override_get_db

        try:
            yield
        finally:
            if saved_get_db:
                app.dependency_overrides[get_db] = old_get_db

    with _session():
        client = TestClient(app)
        client.get("/")


def test_root_with_patch() -> None:
    from unittest.mock import patch

    TestingSessionLocal = create_db(TEST_DB_URL)

    with patch("app.SessionLocal", TestingSessionLocal):
        client = TestClient(app)
        client.get("/")


def test_root_by_hand() -> None:
    # Trying to follow <https://fastapi.tiangolo.com/advanced/testing-database/>
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker

    from app import Base

    engine = create_engine(
        TEST_DB_URL,
        connect_args={"check_same_thread": False},
        native_datetime=True,
    )
    TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
    Base.metadata.create_all(bind=engine)

    def override_get_db():
        try:
            db = TestingSessionLocal()
            yield db
        finally:
            db.close()

    app.dependency_overrides[get_db] = override_get_db
    client = TestClient(app)
    client.get("/")

Description

The above is modeled after these instructions, but those do not appear to work for SQLite’s in-memory databases. Running the following tests results in three errors similar to:

E       sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: app_examples
E       [SQL: SELECT COUNT(*) FROM app_examples]
E       (Background on this error at: https://sqlalche.me/e/14/e3q8)

Changing TEST_DB_URL to, e.g., sqlite:///./test.db allows those tests to pass.

Operating System

Linux, macOS

Operating System Details

No response

FastAPI Version

0.68.1

Python Version

Python 3.9.6

Additional Context

No response

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:7

github_iconTop GitHub Comments

5reactions
jnnnnncommented, Oct 17, 2021

I got this to work following #1223 with:

from sqlalchemy.pool import StaticPool
engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
2reactions
Dentosalcommented, May 21, 2022

Using this solution works for me:

from sqlalchemy.pool import StaticPool
...
engine = create_engine(
    "sqlite:///:memory:", connect_args={"check_same_thread": False},
    poolclass=StaticPool
)
Read more comments on GitHub >

github_iconTop Results From Across the Web

Testing EF Core in Memory using SQLite - Meziantou's blog
A good practice is to test your software. EF Core provides an In-Memory provider to easily test your code without an actual database....
Read more >
EntityFrameworkCore SQLite in-memory db tables are not ...
Ensure that the database for the context exists. If it exists, no action is taken. If it does not exist then the database...
Read more >
Avoid In-Memory Databases for Tests - Jimmy Bogard
The problem we hit is we can't trust the results - you hit cases where the test doesn't work. What we've hit are...
Read more >
How SQLite Is Tested
There are numerous test cases that verify that SQLite is able to deal with malformed database files. These tests first build a well-formed...
Read more >
Using in-memory databases for unit testing EF Core applications
Its because SQLite is a true relational database while EF Core's InMemory database provider isn't a real relational database. This means that EF ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found