Можем ли мы использовать другую БД для запуска pytest? - PullRequest
0 голосов
/ 26 марта 2020

Так что я использую FastAPI для создания своего сервера приложений, у меня есть несколько URL-адресов, которые я выполняю в базе данных. Например, когда я нажимаю URL /createuser/ телом json, он вставляет запись в дБ. Поэтому я использую pytest для проверки этого, но pytest, похоже, использует тот же самый дБ, который используется моим приложением FastAPI. Я хочу создать фиктивную базу данных, чтобы она использовала другую базу данных только для запуска тестов. Я также использовал pytest.fixtures, но думаю, что-то не так. Пожалуйста, помогите с этим. Также я использую postgres в качестве базы данных.

  1. Основное приложение, в котором создается приложение FastAPI
from mock_server.database import engine, SessionLocal, Base

def create_app(connection):
    """This function creates the FastAPI app server.

    Returns:
        app[FastAPI] -- The main app server
    """
    Base.metadata.create_all(bind=connection)
    print("from main:app",connection)
    app = FastAPI(
        title="Masquerader - Mock Server",
        description="""This project allows you to mock any system,
        or service that you wish to."""
    )
    return app

app = create_app(engine)

def get_db():
    """To Get the current db connection.

    Yields:
        [db] -- [The current connection]
    """
    try:
        db = SessionLocal()
        yield db
    finally:
        db.close()

@app.post("/createuser/", response_model=schemas.User)
async def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    """Endpoint for creating a user.

    Arguments:
        user {schemas.UserCreate} -- JSON Body with user details to create

    Keyword Arguments:
        db {Session} -- Current db connection
    """
    user = crud.create_user(db=db, user=user)
    if user is None:
        raise HTTPException(status_code=400, detail="User Already Exists")
    return user
Мой класс базы данных, где определены соединения дБ
from config import current_config
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# sqlalchemy_db_url = "postgresql://fyndlocal:fynd@123@localhost:5432/mockdb"
if os.environ.get("ENV") == "development":
    engine = create_engine(current_config.POSTGRES_MASQUERADER)
if os.environ.get("ENV") is None:
    print("Lol test hai yeh")
    engine = create_engine(current_config.MASQUERADER_LOCAL)
if os.environ.get("ENV") == "pre-production":
    os.environ.__setitem__("POSTGRES_USER", "runner")
    os.environ.__setitem__("POSTGRES_PASS", "")
    os.environ.__setitem__("POSTGRES_HOST", "postgres")
    engine = create_engine(current_config.POSTGRES_TEST_GITLAB)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
Мой класс операций CRUD
def create_user(db: Session, user: schemas.UserCreate):
    """Operation to create a user in the db.

    Arguments:
        db {Session} -- gets current db session
        user {schemas.UserCreate} -- JSON Body that contains
        user details to be created.

    Returns:
        db_user[dict] -- Details of created user.
    """
    hashed_pass = common.hash_generate(user.password)
    old_user = (
        db.query(models.users).filter(models.users.name == user.name).first()
    )
    if old_user is not None:
        return None
    db_user = models.users(
        name=user.name,
        password=hashed_pass,
        is_active=user.is_active,
        is_admin=user.is_admin,
    )
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user
Мой тестовый класс (в котором я хочу использовать фиктивную базу данных)
from main import app
import pytest
from sqlalchemy import create_engine
from starlette.testclient import TestClient
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

@pytest.yield_fixture(scope="module")
def application():
    """Yiest TestClient from FastAPI.

    Yields:
        app[TestClient] -- Testing based application
    """
    SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=cleanup_database)
    db = SessionLocal()
    yield TestClient(app)


@pytest.fixture(scope="module")
def cleanup_database():
    """Creates a mock database for testing purposes.

    Creates a mock database on server for testing and deletes once done.
    """
    username = os.environ.get("POSTGRES_USER", "fyndlocal")
    password = os.environ.get("POSTGRES_PASS", "fynd@123")
    postgres_host = os.environ.get("POSTGRES_HOST", "localhost")
    postgres_port = os.environ.get("POSTGRES_PORT_5432_TCP_PORT", 5432)
    postgres_db = os.environ.get("POSTGRES_DB", "mockdb")
    if not password:
        db_dict = {
            "username": username,
            "password": password,
            "host": postgres_host,
            "port": postgres_port,
            "db": postgres_db,
        }
        default_db_url = current_config.POSTGRES_NOPASS_DSN.format(**db_dict)
        print("if no pass", default_db_url)

    else:
        db_dict = {
            "username": username,
            "password": password,
            "host": postgres_host,
            "port": postgres_port,
            "db": postgres_db,
        }
        default_db_url = current_config.POSTGRES_PASS_DSN.format(**db_dict)
        print("if pass", default_db_url)

    test_masqueraderdb_url = current_config.POSTGRES_TEST_DSN.format(**db_dict)
    print("POSTGRES Config")
    print(db_dict)
    print(test_masqueraderdb_url)
    db_name = test_masqueraderdb_url.split("/")[-1]

    engine = create_engine(default_db_url)
    conn = engine.connect()
    # db.drop_all(engine)
    # db.create_all(engine)
    try:
        conn.execution_options(isolation_level="AUTOCOMMIT").execute(
            f"CREATE DATABASE {db_name}"
        )
    except Exception as e:
        print("this stage", e)

    rv = create_engine(test_masqueraderdb_url)

    db.create_all(rv)
    yield rv
    db.drop_all(rv)

def test_create_user(application,cleanup_database):
    """Test to create user exists in db."""
    response = application.post(
        "/createuser/",
        json={
            "name": "test",
            "is_active": True,
            "is_admin": True,
            "password": "test123",
        },
    )
    expected_resp = {
        "name": "test",
        "is_active": True,
        "is_admin": True,
        "id": 1,
        "urls": [],
    }

    assert response.json() == expected_resp

Следующий тест не пройден, потому что он всегда получает пользователя, присутствующего в моей текущей базе данных.

Спасибо.

1 Ответ

0 голосов
/ 29 марта 2020

Попробуйте переопределить зависимости .

Вы должны придумать прибор, похожий на следующий (непроверенный) код:

@pytest.fixture(scope="module")
def application(cleanup_database):
    def get_mocked_db():
        SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=cleanup_database)
        db = SessionLocal()
        try:
            yield db
        finally:
            db.close() 

    app = create_app(cleanup_database)
    app.dependency_overrides[get_db] = get_mocked_db  # see https://fastapi.tiangolo.com/advanced/testing-dependencies/

    yield TestClient(app)

Обратите внимание, что прибор application зависит от прибора cleanup_database.

...