Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problems with pytest supporting in 0.19.0 #1110

Open
Firezi opened this issue Apr 21, 2022 · 13 comments
Open

Problems with pytest supporting in 0.19.0 #1110

Firezi opened this issue Apr 21, 2022 · 13 comments

Comments

@Firezi
Copy link

Firezi commented Apr 21, 2022

I want to use pytest via fixture like this https://tortoise-orm.readthedocs.io/en/latest/contrib/unittest.html#py-test, but i get ConfigurationError

in conftest.py:

@pytest.fixture(scope="function")
def initialize_db(request):
    initializer(["src.models"], db_url="sqlite://:memory:", app_label="models")
    request.addfinalizer(finalizer)

simple test:

@pytest.mark.asyncio
async def test_user(initialize_db):
    users = await User.all()

But after running test i get:

During handling of the above exception, another exception occurred:

initialize_db = None

    @pytest.mark.asyncio
    async def test_user(initialize_db):
>       users = await User.all()

tests/test_models.py:20: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
venv/lib/python3.10/site-packages/tortoise/models.py:1246: in all
    return cls._meta.manager.get_queryset()
venv/lib/python3.10/site-packages/tortoise/manager.py:15: in get_queryset
    return QuerySet(self._model)
venv/lib/python3.10/site-packages/tortoise/queryset.py:297: in __init__
    super().__init__(model)
venv/lib/python3.10/site-packages/tortoise/queryset.py:94: in __init__
    self.capabilities: Capabilities = model._meta.db.capabilities
venv/lib/python3.10/site-packages/tortoise/models.py:286: in db
    return connections.get(self.default_connection)
venv/lib/python3.10/site-packages/tortoise/connection.py:115: in get
    connection: BaseDBAsyncClient = self._create_connection(conn_alias)
venv/lib/python3.10/site-packages/tortoise/connection.py:90: in _create_connection
    db_info = self._get_db_info(conn_alias)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <tortoise.connection.ConnectionHandler object at 0x102505a50>
conn_alias = 'models'

    def _get_db_info(self, conn_alias: str) -> Union[str, Dict]:
        try:
            return self.db_config[conn_alias]
        except KeyError:
>           raise ConfigurationError(
                f"Unable to get db settings for alias '{conn_alias}'. Please "
                f"check if the config dict contains this alias and try again"
            )
E           tortoise.exceptions.ConfigurationError: Unable to get db settings for alias 'models'. Please check if the config dict contains this alias and try again

venv/lib/python3.10/site-packages/tortoise/connection.py:78: ConfigurationError

I'm getting it only in v0.19.0, for example v0.18.1 works correctly

@LaundroMat
Copy link

LaundroMat commented May 15, 2022

Did you try the code that's in the documentation? I adapted it a bit, and this works for me:

@pytest.fixture(autouse=True)  # Adapted
def client() -> Generator:
    initializer(["app.models.files", "app.models.users"], db_url="sqlite://:memory:", app_label="models")
    with TestClient(app) as c:
        yield c
    finalizer()

@shubham-betterworks
Copy link

We went with this workaround till the issue is fixed

@pytest.fixture(scope="session", autouse=True)
def db(request: SubRequest) -> None:
    config = getTestDBConfig(app_label="models", modules=DB_MODELS)

    async def _init_db() -> None:
        await Tortoise.init(config)
        try:
            await Tortoise._drop_databases()
        except (DBConnectionError, OperationalError):  # pragma: nocoverage
            pass

        await Tortoise.init(config, _create_db=True)
        await Tortoise.generate_schemas(safe=False)

    loop = asyncio.get_event_loop()
    loop.run_until_complete(_init_db())

    request.addfinalizer(lambda: loop.run_until_complete(Tortoise._drop_databases()))

@luyiming
Copy link
Contributor

luyiming commented Jun 18, 2022

same issue here, I guess there's something to do with this PR #1001? Downgrading to 0.18.1 fixes this issue.

@TheBubblePopped
Copy link

Same here

@igor-zmitrovich
Copy link

I have the same issue

@awtkns
Copy link

awtkns commented Jan 28, 2023

Same issue for me. As @luyiming downgrading to 0.18.1 seems to fix the issue. Example causing test suite to fail after upgrading to 0.19

@Cikmo
Copy link

Cikmo commented Mar 21, 2023

Any updates on this?

@sodrian
Copy link

sodrian commented Apr 25, 2023

I see 0.19.3 has partial solution in it, so for me workaround is as follows:

from tortoise.contrib.test import _init_db, getDBConfig

@pytest.fixture(scope='session', autouse=True)
def in_memory_db(request):
    config = getDBConfig(app_label="models", modules=['your_lib.models'])

    loop = asyncio.get_event_loop()
    loop.run_until_complete(_init_db(config))

    request.addfinalizer(lambda: loop.run_until_complete(Tortoise._drop_databases()))

@stuaxo
Copy link

stuaxo commented May 11, 2023

I spent the last two days on a tiny app with one table to reproducing this, and I just cannot work out how tortoise orm, pytest and fastapi I supposed to work together.

The default suggestion of creating an app directly in a module means it gets created with that ORM initialised before the pytest gets in there.

All the solutions I've seen don't seem to work - it just seems like such a foot gun, this seems way too complicated and the async nature means working out the order of things is very painful.

I wonder if seperating the place where the app is created from the place the orm is connected to it might help - register_tortoise, and the testclient equivalent just seem to be fighting now.

There are a lot of people hitting this issue if you search for the similar search terms - maybe there is a design issue making this hard, when it really should be something you can't get wrong easily.

@Tondejphajin
Copy link

@stuaxo I found fastAPI and tortoise example (https://tortoise.github.io/examples/fastapi.html) that uses pytest to run the test. It seems to work fine for me.

@stuaxo
Copy link

stuaxo commented May 30, 2023

Thanks for posting that - I was many many hours into a "3 hour" exercise I had taken on for an interview when I hit the wall on this; hopefully someone will find that link helpful (or me in the future).

@kleynjan
Copy link

For 0.20.0 I struggled to find a workaround that actually works. @sodrian's came closest, but I still got an 'Event loop is closed' error. In my env this does the trick:

import asyncio
from tortoise import Tortoise
from tortoise.contrib.test import _init_db, getDBConfig

# https://stackoverflow.com/questions/61022713/pytest-asyncio-has-a-closed-event-loop-but-only-when-running-all-tests

@pytest.fixture(scope="session")
def event_loop():
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = asyncio.new_event_loop()
    yield loop
    loop.close()

@pytest.fixture(scope="session")
def in_memory_db(request, event_loop):
    config = getDBConfig(app_label="models", modules=["models.models"])
    event_loop.run_until_complete(_init_db(config))
    request.addfinalizer(lambda: event_loop.run_until_complete(Tortoise._drop_databases()))

# actual test, using pytest-asyncio plugin
@pytest.mark.asyncio
async def test_create(in_memory_db):
    await MyModel(....).save()
    mm = await MyModel.first()
    assert mm is not None

@gxpd-jjh
Copy link

gxpd-jjh commented Jan 8, 2024

For 0.20.0 I struggled to find a workaround that actually works. @sodrian's came closest, but I still got an 'Event loop is closed' error. In my env this does the trick:

import asyncio
from tortoise import Tortoise
from tortoise.contrib.test import _init_db, getDBConfig

# https://stackoverflow.com/questions/61022713/pytest-asyncio-has-a-closed-event-loop-but-only-when-running-all-tests

@pytest.fixture(scope="session")
def event_loop():
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = asyncio.new_event_loop()
    yield loop
    loop.close()

@pytest.fixture(scope="session")
def in_memory_db(request, event_loop):
    config = getDBConfig(app_label="models", modules=["models.models"])
    event_loop.run_until_complete(_init_db(config))
    request.addfinalizer(lambda: event_loop.run_until_complete(Tortoise._drop_databases()))

# actual test, using pytest-asyncio plugin
@pytest.mark.asyncio
async def test_create(in_memory_db):
    await MyModel(....).save()
    mm = await MyModel.first()
    assert mm is not None

Thanks to @kleynajn, here is what mine became:

@pytest.fixture(autouse=True)
def initialize_test_db(request, event_loop):
    config = getDBConfig(app_label="models", modules=DB_MODELS)

    event_loop.run_until_complete(_init_db(config))

    request.addfinalizer(
        lambda: event_loop.run_until_complete(Tortoise._drop_databases())
    )


@pytest.fixture(scope="module")
def event_loop():
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests