Question:
- How to run tests in isolation that don't create entries in database.- Also, How we can use the isolated user that is created during the test for other endpoint testing
Some minimal code:
from contextlib import asynccontextmanagerimport databasesimport sqlalchemyfrom fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModel, EmailStrDATABASE_URL = "postgresql://user:password@postgresserver/db"database = databases.Database(DATABASE_URL)metadata = sqlalchemy.MetaData()users = sqlalchemy.Table("users", metadata, sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True, index=True), sqlalchemy.Column("first_name", sqlalchemy.String, nullable=True), sqlalchemy.Column("last_name", sqlalchemy.String, nullable=True), sqlalchemy.Column("email", sqlalchemy.String, index=True, nullable=False, unique=True), sqlalchemy.Column("password", sqlalchemy.String, index=True, nullable=False),)engine = sqlalchemy.create_engine( DATABASE_URL)metadata.create_all(engine)class UserBase(BaseModel): first_name: str | None last_name: str | None email: EmailStrclass UserCreate(UserBase): password: strapp = FastAPI()@asynccontextmanagerasync def lifespan(app: FastAPI): await database.connect() yield await database.disconnect()app = FastAPI(lifespan=lifespan)class UserRepository: async def create(self, obj_in: UserCreate): query = users.insert().values( first_name=obj_in.first_name, last_name=obj_in.last_name, email=obj_in.email, password=obj_in.password, ) await database.execute(query=query) return obj_in async def get_by_email(self, email: str): query = users.select().where(email == users.c.email) return await database.fetch_one(query=query)user_repository = UserRepository()@app.post("/", response_model=UserBase)async def create_user( payload: UserCreate,): if await user_repository.get_by_email(payload.email): raise HTTPException( status_code=400, detail="The user with this username already exists in the database.", ) return await user_repository.create(payload)##### Testing import pytestfrom fastapi.testclient import TestClient@pytest.fixture()def client(): with TestClient(app) as c: yield c@pytest.fixture(scope="session")async def db(): database = databases.Database(DATABASE_URL, force_rollback=True) await database.connect() yield database await database.disconnect()async def test_create_user_endpoint(client: TestClient): user_data = UserCreate( first_name="John", last_name="Doe", email="JohnDoe@example.in", password="pass123", ) response = client.post("/", json=user_data.dict()) assert response.status_code == 200 created_user = response.json() assert created_user["first_name"] == "John" assert created_user["last_name"] == "Doe"
The issue at involves unexpected behavior when running a FastAPI test multiple times using Pytest. The intention is to ensure test isolation by utilizing the force_rollback=True option in the test database fixture. However, despite using this option, the test fails on subsequent runs, indicating that the user already exists in the database.