-
-
Notifications
You must be signed in to change notification settings - Fork 392
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
Fastapi example test not working #1029
Comments
I have the same problem. Python: 3.9.7 |
Same issue. Python 3.8.8 |
You can get at least the test passing by swapping the function with
Disclaimer: I have no idea if this breaks something. But I'm guessing it's OK to not use starlette's test client event loop for the DB stuff. |
That one worked for me, the tests pass, but the consequences are still unknown. Can anyone who disliked the comment explain me why this decision is bad/wrong if it works? |
voneiden's approach still gave me a warning with Python 3.10:
I had a look at The event loop is instantiated in As said in their docs, we would have to write the
All we need to do is add a # conftest.py
@pytest.fixture(scope="module")
def event_loop() -> Iterator[asyncio.AbstractEventLoop]:
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
# Test client
@pytest.fixture(scope="module")
def client(event_loop: asyncio.BaseEventLoop) -> Iterator[TestClient]:
initializer(TORTOISE_ORM["apps"]["models"]["models"], loop=event_loop)
with TestClient(app) as c:
yield c
finalizer() As I was writing my tests, I found out that Tortoise expected the app to have the same configuration as tests. If you connect to a separate database by default, or don't set
Code tweaks# app.py
import init_db from db
app = FastAPI()
# Your app code...
init_db(app) # db.py
TORTOISE_ORM = {
"connections": {"default": f"postgres://{DATABASE_USERNAME}:{DATABASE_PASSWORD}@{DATABASE_HOST}:{DATABASE_PORT}/{DATABASE_NAME}"},
"apps": {
"models": {
"models": [
# Aerich migrations
"aerich.models",
# Our custom models
"models"
],
"default_connection": "default",
},
},
}
generate_schemas = False
def switch_to_test_mode():
global TORTOISE_ORM, generate_schemas
TORTOISE_ORM['connections']['default'] = 'sqlite://:memory:'
generate_schemas = True
def init(app: FastAPI):
register_tortoise(
app,
TORTOISE_ORM,
add_exception_handlers=True,
generate_schemas=generate_schemas
) # tests/conftest.py
from models import TORTOISE_ORM, switch_to_test_mode
switch_to_test_mode() # Tortoise ORM hack - has to be done before importing app
import app from app
# ... |
Got it working with @TPXP's excellent guide and explanation, thanks! (Also thanks for the test db config, I needed that too!) |
@vjousse You can see my template. There is work tests. @ada0l/fastapi_tortoise_aerich_template |
Went down quite the rabbit-hole with this one. Okay. So it appears the change that breaks this example was introduced in Starlette v0.15.0. Specifically, here's the PR that introduced the change: encode/starlette#1157. The release notes offer the following brief explanation: "Starlette now supports Trio as an async runtime via AnyIO". The result was an abstraction of the previous async implementation via AnyIO so that a number of async backends could be supported. That meant that the Type of The good news is, the same pattern is still roughly available. Its just that the names have changed. To implement AnyIO, a The code ends up looking like this: ...
@pytest.fixture(scope="module")
def blocking_portal(client: TestClient) -> Iterator[BlockingPortal]:
yield client.portal
def test_create_user(client: TestClient, blocking_portal: BlockingPortal): # nosec
response = client.post("/users", json={"username": "admin"})
assert response.status_code == 200, response.text
data = response.json()
assert data["username"] == "admin"
assert "id" in data
user_id = data["id"]
async def get_user_by_db():
user = await Users.get(id=user_id)
return user
user_obj = blocking_portal.call(get_user_by_db)
assert user_obj.id == user_id Of course at this point, I don't really see the point of a separate def test_create_user(client: TestClient): # nosec
response = client.post("/users", json={"username": "admin"})
assert response.status_code == 200, response.text
data = response.json()
assert data["username"] == "admin"
assert "id" in data
user_id = data["id"]
async def get_user_by_db():
user = await Users.get(id=user_id)
return user
user_obj = client.portal.call(get_user_by_db)
assert user_obj.id == user_id It seems like this is the most consistent and least hacky option available to obtain the event loop (or event-loop-like object) from the I'll let this stew for a few days, but I plan on submitting a PR with this change if no one can educate me further on this. |
@abondar This issue can be marked as completed. |
Describe the bug
The Fastapi example test is not working.
To Reproduce
Go to the directory
examples/fastapi
and run the tests with:Steps to reproduce the behavior, preferaby a small code snippet.
Expected behavior
I would expect the test to be green.
The text was updated successfully, but these errors were encountered: