简体   繁体   中英

how to rollback for each test using pytest, fastapi and tortoise-orm?

I'm coding the unit tests for a crud, the framework I'm using is FastAPI, the ORM is tortoise and the module for testing is pytest.

I have this configuration file:

import os

import pytest
from starlette.testclient import TestClient
from tortoise.contrib.fastapi import register_tortoise

from app.config import Settings, get_settings
from app.main import create_application


def get_settings_override():
    return Settings(
        testing=1, database_url=os.environ.get("DATABASE_TEST_URL")
    )

@pytest.fixture(scope="module")
def test_app_with_db():
    # set up
    app = create_application()
    app.dependency_overrides[get_settings] = get_settings_override

    # Link with DB for testing
    register_tortoise(
        app,
        db_url=os.environ.get("DATABASE_TEST_URL"),
        modules={"models": ["app.infra.postgres.models"]},
        generate_schemas=True,
        add_exception_handlers=True,
    )
    with TestClient(app) as test_client:

        # testing
        yield test_client

    # tear down

Also, I have these tests, the first one creates a new carrier. The second one, creates a carrier and then searches all the existing carriers in the database.

import json
from app.infra.postgres.crud.cft import cft as pg_cft
from app.services.cft import CFTService

cft_crud = CFTService(pg_cft)

PREFIX = "/api/cft/"


def test_create_carrier(test_app_with_db):
    response = test_app_with_db.post(
        f"{PREFIX}",
        data=json.dumps(
            {
                "broker_id": 1,
                "carrier_id": 1,
                "lower_limit": 10,
                "upper_limit": 100,
                "fee": 50,
                "is_active": True,
            }
        ),
    )
    assert response.status_code == 201
    assert type(response.json()["id"]) == int


def test_get_carriers(test_app_with_db):
    response = test_app_with_db.post(
        f"{PREFIX}",
        data=json.dumps(
            {
                "broker_id": 1,
                "carrier_id": 1,
                "lower_limit": 10,
                "upper_limit": 100,
                "fee": 50,
                "is_active": True,
            }
        ),
    )
    summary_id = response.json()["id"]

    response = test_app_with_db.get(f"{PREFIX}")
    assert response.status_code == 200

    response_list = response.json()

    assert (
        len(list(filter(lambda d: d["id"] == summary_id, response_list))) == 1
    )

The problem is that when I run the tests with the command docker-compose -f Docker-compose.dev.yml exec web python -m pytest (being web the name of the container) I get an error because there is already the combination of broker_id and carrier_id. What I want is for the database to be restored for each of the tests. How can I do that?

Edit:

That's how I managed to do what I wanted:

import os

import pytest
from starlette.testclient import TestClient
from tortoise.contrib.test import finalizer, initializer

from app.config import Settings, get_settings
from app.main import create_application


def get_settings_override():
    return Settings(testing=1, database_dev_url=os.environ.get("DATABASE_TEST_URL"))


@pytest.fixture(scope="function")
def test_app():
    # set up
    app = create_application()
    app.dependency_overrides[get_settings] = get_settings_override

    with TestClient(app) as test_client:

        # testing
        yield test_client

    # tear down


@pytest.fixture(scope="function")
def test_app_with_db():
    # set up
    app = create_application()
    app.dependency_overrides[get_settings] = get_settings_override

    # Link with DB for testing
    initializer(
        ["app.infra.postgres.models"],
        db_url=os.environ.get("DATABASE_TEST_URL"),
    )

    with TestClient(app) as test_client:
        # testing
        yield test_client

    # tear down
    finalizer()

As MrBean said, you need to make the fixture run for every test, rather than for every module.

You can change the scope like this:

@pytest.fixture(scope="function")
def test_app_with_db():
    # set up
...

Or you can remove the scope parameter altogether, because it defaults to function :

https://docs.pytest.org/en/stable/fixture.html#scope-sharing-fixtures-across-classes-modules-packages-or-session

This is assuming that you actually have something in your # teardown section of the fixture. Otherwise there's nothing to clean the database.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM