-
-
Notifications
You must be signed in to change notification settings - Fork 343
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
Question: How to override a dependency using create_test_client
#2597
Comments
I have changed my tests using suggestions from: https://github.com/orgs/litestar-org/discussions/1789#discussioncomment-6110017
from models import TodoItem
TODO_LIST: list[TodoItem] = [
TodoItem(title="Start writing TODO list", done=True),
TodoItem(title="???", done=False),
TodoItem(title="Profit", done=False),
]
def get_todo_list():
return TODO_LIST
@post("/")
async def add_item(todo_list: Annotated[list[TodoItem], Dependency], data: TodoItem) -> list[TodoItem]:
todo_list.append(data)
return todo_list
def get_todo_by_title(todo_name, todo_list) -> TodoItem:
for item in todo_list:
if item.title == todo_name:
return item
raise NotFoundException(detail=f"TODO {todo_name!r} not found")
@get("/{item_title:str}")
async def get_item(todo_list: Annotated[list[TodoItem], Dependency], item_title: str) -> TodoItem:
return get_todo_by_title(item_title, todo_list)
app = Litestar([get_item, add_item], dependencies={"todo_list": Provide(get_todo_list)})
from litestar.testing import TestClient
def test_create_and_get_item(mocker):
items = []
def get_todo_list_mock():
return items
mocker.patch('dependencies.get_todo_list', new=get_todo_list_mock)
import app
with TestClient(app=app.app) as test_client:
task_name = 'new'
item = {'title': task_name, 'done': False, 'deadline': 4}
r = test_client.post("/", json=item)
assert r.status_code == 201
r2 = test_client.get(f"/{task_name}")
assert r2.status_code == 200
assert r2.json() == item The result is the same. |
Hey @dybi - sorry about the delayed response here. Did you read this: https://github.com/orgs/litestar-org/discussions/1789#discussioncomment-6110017 I think an appropriately used application factory pattern is what you need. |
hey @peterschutt , I have rewritten the code to use
from dataclasses import dataclass
from typing import Annotated
import msgspec
@dataclass
class TodoItem:
title: str
done: bool
deadline: Annotated[int, msgspec.Meta(ge=1)] | None = None
def __post_init__(self):
if self.done and self.deadline is not None:
raise ValueError("Done tasks cannot have a deadline")
TODO_LIST: list[TodoItem] = list([
TodoItem(title="Start writing TODO list", done=True),
TodoItem(title="???", done=False),
TodoItem(title="Profit", done=False),
])
async def get_todo_list():
return TODO_LIST
from litestar import Litestar, get, post
from litestar.di import Provide
from litestar.exceptions import NotFoundException
from dependencies import get_todo_list, TodoItem
@post("/")
async def add_item( data: TodoItem, todo_list: list[TodoItem]) -> list[TodoItem]:
todo_list.append(data)
return todo_list
def get_todo_by_title(todo_name, todo_list) -> TodoItem:
for item in todo_list:
if item.title == todo_name:
return item
raise NotFoundException(detail=f"TODO {todo_name!r} not found")
@get("/{item_title:str}")
async def get_item( item_title: str, todo_list: list[TodoItem]) -> TodoItem:
return get_todo_by_title(item_title, todo_list)
def create_app():
app = Litestar([get_item, add_item], dependencies={"todo_list": Provide(get_todo_list)})
return app
import pytest
from litestar.testing import TestClient
from app import create_app
items = []
def get_todo_list_mock():
return items
@pytest.fixture(autouse=True)
def mock_list(mocker):
mocker.patch('app.get_todo_list', new=get_todo_list_mock)
@pytest.fixture
def app():
return create_app()
def test_create_and_get_item(app):
with TestClient(app) as test_client:
task_name = 'new'
item = {'title': task_name, 'done': False, 'deadline': 4}
r = test_client.post("/", json=item)
assert r.status_code == 201
r2 = test_client.get(f"/{task_name}")
assert r2.status_code == 200
assert r2.json() == item The result is the same as the underlying problem remains the same - in https://github.com/litestar-org/litestar/blob/0bd5feb84ac475cdaaa53ce232b084d0c5105cc8/litestar/routes/http.py#L187C3-L189 |
OK thanks for persisting with us, I have reproduced. This is a recurring theme with our dependencies. The value that is returned from the dependency provider is validated according to the annotation on the handlers. This prevents a class of user error where the provider for a dependency doesn't return the type that the user has declared at the point where the dependency is used. We use Anyway, for now you can get your example to work by telling litestar to explicitly not validate the from typing import Annotated
from litestar.params import Dependency
...
@post("/")
async def add_item( data: TodoItem, todo_list: Annotated[list[TodoItem], Dependency(skip_validation=True)]) -> list[TodoItem]:
todo_list.append(data)
return todo_list
...
@get("/{item_title:str}")
async def get_item( item_title: str, todo_list: Annotated[list[TodoItem], Dependency(skip_validation=True)]) -> TodoItem:
return get_todo_by_title(item_title, todo_list) Edit: both deps should skip validation |
Thanks for the answers. The above works (with small changes: either flip the validation requirements, i.e. Out of curiosity I have prepared another version of app with intermediate Thanks for your help :) If I may suggest something - from user's perspective it would be good to have the above behaviour somehow documented ;) Keep up the good work! 💪 |
create_test_client
create_test_client
Description
I have been playing with https://docs.litestar.dev/2/tutorials/todo-app/3-assembling-the-app.html#final-application and wanted to create some tests for it.
Namely, I wanted a test that after I create a
TodoItem
I am able to get it by name.To do that, I have created a test client using
create_test_client
and providedroute_handlers
from theapp
. Also, I have changed the"todo_list"
dependency so it uses a local function that provides a test globalITEMS = []
(I had been also trying with fixture localitesms = []
with the same results).At some point during injecting dependecies global
ITEMS
gets copied (theid()
of the object changes) and my route handleget_item
get differenttodo_list
thenadd_item
, so the item that has been appened inadd_item
is not visible inget_item
.URL to code causing the issue
No response
MCVE
Screenshots
No response
Logs
No response
Litestar Version
2.2.1
Platform
Note
While we are open for sponsoring on GitHub Sponsors and
OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.
Check out all issues funded or available for funding on our Polar.sh Litestar dashboard
The text was updated successfully, but these errors were encountered: