FastAPI + pytest unable to clean Django ORM

Question:

I’m creating a FastAPI project that integrates with the Django ORM. When running pytest though, the PostgreSQL database is not rolling back the transactions. Switching to SQLite, the SQLite database is not clearing the transactions, but it is tearing down the db (probably because SQLite uses in-memory db). I believe pytest-django is not calling the rollback method to clear the database.

In my pytest.ini, I have the --reuse-db flag on.

Here’s the repo: https://github.com/Andrew-Chen-Wang/fastapi-django-orm which includes pytest-django and pytest-asyncio If anyone’s done this with flask, that would help too.

Assuming you have PostgreSQL:

Steps to reproduce:

  1. sh bin/create_db.sh which creates a new database called testorm
  2. pip install -r requirements/local.txt
  3. pytest tests/

The test is calling a view that creates a new record in the database tables and tests whether there is an increment in the number of rows in the table:

# In app/core/api/a_view.py
@router.get("/hello")
async def hello():
    await User.objects.acreate(name="random")
    return {"message": f"Hello World, count: {await User.objects.acount()}"}


# In tests/conftest.py
import pytest
from httpx import AsyncClient

from app.main import fast

@pytest.fixture()
def client() -> AsyncClient:
    return AsyncClient(app=fast, base_url="http://test")

# In tests/test_default.py
async def test_get_hello_view(client):
    """Tests whether the view can use a Django model"""
    old_count = await User.objects.acount()
    assert old_count == 0
    async with client as ac:
        response = await ac.get("/hello")
    assert response.status_code == 200
    new_count = await User.objects.acount()
    assert new_count == 1
    assert response.json() == {"message": "Hello World, count: 1"}

async def test_clears_database_after_test(client):
    """Testing whether Django clears the database"""
    await test_get_hello_view(client)

The first test case passes but the second doesn’t. If you re-run pytest, the first test case also starts not passing because the test database is not clearing the transaction from the first run.

I adjusted the test to not include the client call, but it seems like pytest-django is simply not creating a transaction around the Django ORM, because the db is not being cleared for each test:

async def test_get_hello_view(client):
    """Tests whether the view can use a Django model"""
    old_count = await User.objects.acount()
    assert old_count == 0
    await User.objects.acreate(name="test")
    new_count = await User.objects.acount()
    assert new_count == 1


async def test_clears_database_after_test(client):
    """Testing whether Django clears the database"""
    await test_get_hello_view(client)

How should I clear the database for each test case?

Asked By: acw

||

Answers:

Turns out my pytest.mark.django_db just needed transaction=True in it like pytest.mark.django_db(transaction=True)

Answered By: acw
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.