mirror of https://github.com/tiangolo/fastapi.git
224 lines
6.5 KiB
Python
224 lines
6.5 KiB
Python
from abc import ABC, abstractmethod
|
|
from typing import List, runtime_checkable
|
|
|
|
import pytest
|
|
from fastapi import APIRouter, FastAPI, Service
|
|
from fastapi.testclient import TestClient
|
|
from pydantic import BaseModel
|
|
from typing_extensions import Protocol
|
|
|
|
app = FastAPI()
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class Item(BaseModel):
|
|
name: str
|
|
value: int
|
|
|
|
|
|
@runtime_checkable
|
|
class ItemsProtocol(Protocol):
|
|
def get_items(self) -> List[Item]:
|
|
... # pragma: nocover
|
|
|
|
|
|
class ItemsInterface(ABC):
|
|
@abstractmethod
|
|
def get_items(self) -> List[Item]:
|
|
... # pragma: nocover
|
|
|
|
|
|
class ItemsService(ItemsInterface):
|
|
def __init__(self, name: str, value: int):
|
|
self.name = name
|
|
self.value = value
|
|
|
|
def get_items(self) -> List[Item]:
|
|
return [Item(name=self.name, value=self.value)]
|
|
|
|
|
|
class ClassNestedProtocol:
|
|
def __init__(self, impl: ItemsProtocol = Service()):
|
|
self.impl = impl
|
|
|
|
|
|
class ClassNestedInterface:
|
|
def __init__(self, impl: ItemsInterface = Service()):
|
|
self.impl = impl
|
|
|
|
|
|
class ClassNested:
|
|
def __init__(self, impl: ItemsService = Service()):
|
|
self.impl = impl
|
|
|
|
|
|
@app.get("/depends-on-protocol/")
|
|
async def depends_on_protocol(service: ItemsProtocol = Service()):
|
|
return {"in": "depends-on-protocol", "items": service.get_items()}
|
|
|
|
|
|
@app.get("/depends-on-interface/")
|
|
async def depends_on_interface(service: ItemsInterface = Service()):
|
|
return {"in": "depends-on-interface", "items": service.get_items()}
|
|
|
|
|
|
@app.get("/depends-on-class/")
|
|
async def depends_on_class(service: ItemsService = Service()):
|
|
return {"in": "depends-on-class", "items": service.get_items()}
|
|
|
|
|
|
@app.get("/depends-on-nested-protocol/")
|
|
async def depends_on_nested_protocol(service: ClassNestedProtocol = Service()):
|
|
return {"in": "depends-on-nested-protocol", "items": service.impl.get_items()}
|
|
|
|
|
|
@app.get("/depends-on-nested-interface/")
|
|
async def depends_on_nested_interface(service: ClassNestedInterface = Service()):
|
|
return {"in": "depends-on-nested-interface", "items": service.impl.get_items()}
|
|
|
|
|
|
@app.get("/depends-on-nested-class/")
|
|
async def depends_on_nested_class(service: ClassNested = Service()):
|
|
return {"in": "depends-on-nested-class", "items": service.impl.get_items()}
|
|
|
|
|
|
app.include_router(router)
|
|
|
|
client = TestClient(app)
|
|
|
|
|
|
def test_depends_on_protocol_no_override():
|
|
with pytest.raises(TypeError, match="Protocols cannot be instantiated"):
|
|
# not 422 error about missing args and kwargs inputs
|
|
client.get("/depends-on-protocol/")
|
|
|
|
|
|
def test_depends_on_protocol_override():
|
|
app.dependency_overrides[ItemsProtocol] = lambda: ItemsService(name="abc", value=1)
|
|
response = client.get("/depends-on-protocol/")
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"in": "depends-on-protocol",
|
|
"items": [
|
|
{
|
|
"name": "abc",
|
|
"value": 1,
|
|
},
|
|
],
|
|
}
|
|
app.dependency_overrides = {}
|
|
|
|
|
|
def test_depends_on_interface_no_override():
|
|
error_msg = "Can't instantiate abstract class ItemsInterface with.*abstract method"
|
|
with pytest.raises(TypeError, match=error_msg):
|
|
client.get("/depends-on-interface/")
|
|
|
|
|
|
def test_depends_on_interface_override():
|
|
app.dependency_overrides[ItemsInterface] = lambda: ItemsService(name="abc", value=1)
|
|
response = client.get("/depends-on-interface/")
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"in": "depends-on-interface",
|
|
"items": [
|
|
{
|
|
"name": "abc",
|
|
"value": 1,
|
|
},
|
|
],
|
|
}
|
|
app.dependency_overrides = {}
|
|
|
|
|
|
def test_depends_on_class_no_override():
|
|
error_msg = "missing 2 required positional arguments: 'name' and 'value'"
|
|
with pytest.raises(TypeError, match=error_msg):
|
|
# not 422 error about missing body fields
|
|
client.get("/depends-on-class/")
|
|
|
|
|
|
def test_depends_on_class_override():
|
|
app.dependency_overrides[ItemsService] = lambda: ItemsService(name="abc", value=1)
|
|
response = client.get("/depends-on-class/")
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"in": "depends-on-class",
|
|
"items": [
|
|
{
|
|
"name": "abc",
|
|
"value": 1,
|
|
},
|
|
],
|
|
}
|
|
app.dependency_overrides = {}
|
|
|
|
|
|
def test_depends_on_nested_protocol_no_override():
|
|
with pytest.raises(TypeError, match="Protocols cannot be instantiated"):
|
|
client.get("/depends-on-nested-protocol/")
|
|
|
|
|
|
def test_depends_on_nested_protocol_override_top_level():
|
|
service = ClassNestedProtocol(impl=ItemsService(name="abc", value=1))
|
|
app.dependency_overrides[ClassNestedProtocol] = lambda: service
|
|
response = client.get("/depends-on-nested-protocol/")
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"in": "depends-on-nested-protocol",
|
|
"items": [
|
|
{
|
|
"name": "abc",
|
|
"value": 1,
|
|
},
|
|
],
|
|
}
|
|
app.dependency_overrides = {}
|
|
|
|
|
|
def test_depends_on_nested_interface_no_override():
|
|
error_msg = "Can't instantiate abstract class ItemsInterface with.*abstract method"
|
|
with pytest.raises(TypeError, match=error_msg):
|
|
client.get("/depends-on-nested-interface/")
|
|
|
|
|
|
def test_depends_on_nested_interface_override_top_level():
|
|
service = ClassNestedInterface(impl=ItemsService(name="abc", value=1))
|
|
app.dependency_overrides[ClassNestedInterface] = lambda: service
|
|
response = client.get("/depends-on-nested-interface/")
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"in": "depends-on-nested-interface",
|
|
"items": [
|
|
{
|
|
"name": "abc",
|
|
"value": 1,
|
|
},
|
|
],
|
|
}
|
|
app.dependency_overrides = {}
|
|
|
|
|
|
def test_depends_on_nested_class_no_override():
|
|
error_msg = "missing 2 required positional arguments: 'name' and 'value'"
|
|
with pytest.raises(TypeError, match=error_msg):
|
|
client.get("/depends-on-nested-class/")
|
|
|
|
|
|
def test_depends_on_nested_class_override_top_level():
|
|
service = ClassNested(impl=ItemsService(name="abc", value=1))
|
|
app.dependency_overrides[ClassNested] = lambda: service
|
|
response = client.get("/depends-on-nested-class/")
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"in": "depends-on-nested-class",
|
|
"items": [
|
|
{
|
|
"name": "abc",
|
|
"value": 1,
|
|
},
|
|
],
|
|
}
|
|
app.dependency_overrides = {}
|