Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/api_controller/model_controller/04_parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,6 @@ class EventModelController(ModelControllerBase):

list_events = ModelEndpointFactory.list(
path="/",
schema_in=EventQueryParams,
schema_out=EventSchema,
queryset_getter=lambda self, query: self.service.get_filtered_events(query)
)
Expand Down
68 changes: 66 additions & 2 deletions docs/tutorial/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class TestMyMathController:
}

```
Similarly, for testing an asynchronous route function, you can use TestClientAsync as follows:
Similarly, for testing an asynchronous route function, you can use TestAsyncClient as follows:

```python
from ninja_extra import api_controller, route
Expand All @@ -50,8 +50,72 @@ class MyMathController:
class TestMyMathController:
def test_get_users_async(self):
client = TestAsyncClient(MyMathController)
response = client.get('/add', query=dict(a=3, b=5))
response = client.get('/add', query={"a": 3, "b": 5})
assert response.status_code == 200
assert response.json() == {"result": -2}

```

### Controllers with a static prefix

When using `TestClient`/`TestAsyncClient` with a controller class that has a static prefix, call endpoints using the route path defined on the method (without the static prefix). The testing client wires the controller under the hood and resolves routes accordingly.

```python
from ninja_extra import api_controller, route
from ninja_extra.testing import TestClient


@api_controller('/api', tags=['Users'])
class UserController:
@route.get('/users')
def list_users(self):
return [
{
'first_name': 'Ninja Extra',
'username': 'django_ninja',
'email': '[email protected]',
}
]


def test_get_users():
client = TestClient(UserController)
# Note: no '/api' here
response = client.get('/users')
assert response.status_code == 200
assert response.json()[0]['username'] == 'django_ninja'
```

### Controllers with a path variable in the prefix

If the controller prefix contains path parameters, include those parameters in the URL when calling the testing client. For example:

```python
import uuid
from ninja import Schema
from ninja_extra import api_controller, route
from ninja_extra.testing import TestClient


class UserIn(Schema):
username: str
email: str


@api_controller('/users/{int:org_id}/', tags=['Users'])
class OrgUsersController:
@route.post('')
def create_user(self, org_id: int, user: UserIn):
# simulate created user
return {'id': str(uuid.uuid4()), 'org_id': org_id, 'username': user.username}


def test_create_user_under_param_prefix():
client = TestClient(OrgUsersController)
response = client.post('/users/123/', json={'username': 'jane', 'email': '[email protected]'})
assert response.status_code == 200
data = response.json()
assert data['org_id'] == 123
assert data['username'] == 'jane'
assert 'id' in data
```
118 changes: 118 additions & 0 deletions tests/test_testclient_with_controllers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import uuid

import django
import pytest
from ninja import Schema

from ninja_extra import api_controller, route
from ninja_extra.testing import TestAsyncClient, TestClient


@api_controller("", tags=["Users"])
class UserController:
@route.get("/users")
def list_users(self):
return [
{
"first_name": "Ninja Extra",
"username": "django_ninja",
"email": "[email protected]",
}
]


class TestClientWithController:
def test_get_users(self):
client = TestClient(UserController)
response = client.get("/users")
assert response.status_code == 200
body = response.json()
assert isinstance(body, list) and len(body) == 1
assert body[0] == {
"first_name": "Ninja Extra",
"username": "django_ninja",
"email": "[email protected]",
}


@api_controller("", tags=["Math"])
class MyMathController:
@route.get("/add")
async def add(self, a: int, b: int):
"""Return a - b to mirror the docs example."""
return {"result": a - b}


class TestAsyncClientWithController:
@pytest.mark.skipif(django.VERSION < (3, 1), reason="requires django 3.1 or higher")
@pytest.mark.asyncio
async def test_add_async(self):
client = TestAsyncClient(MyMathController)
response = await client.get("/add", query={"a": 3, "b": 5})
assert response.status_code == 200
assert response.json() == {"result": -2}


@api_controller("/api", tags=["Users"])
class PrefixedUserController:
@route.get("/users")
def list_users(self):
return [
{
"first_name": "Ninja Extra",
"username": "django_ninja",
"email": "[email protected]",
}
]


class TestClientWithPrefixedController:
def test_get_users_under_prefix(self):
client = TestClient(PrefixedUserController)
response = client.get("/users")
assert response.status_code == 200
body = response.json()
assert isinstance(body, list) and len(body) == 1
assert body[0]["username"] == "django_ninja"


@api_controller("/math", tags=["Math"])
class PrefixedMathController:
@route.get("/add")
async def add(self, a: int, b: int):
return {"result": a + b}


class TestAsyncClientWithPrefixedController:
@pytest.mark.skipif(django.VERSION < (3, 1), reason="requires django 3.1 or higher")
@pytest.mark.asyncio
async def test_add_async_under_prefix(self):
client = TestAsyncClient(PrefixedMathController)
response = await client.get("/add", query={"a": 3, "b": 5})
assert response.status_code == 200
assert response.json() == {"result": 8}


class UserIn(Schema):
username: str
email: str


@api_controller("/users/{int:org_id}/", tags=["Users"])
class OrgUsersController:
@route.post("")
def create_user(self, org_id: int, user: UserIn):
return {"id": str(uuid.uuid4()), "org_id": org_id, "username": user.username}


class TestClientWithParamPrefixedController:
def test_create_user_under_param_prefix(self):
client = TestClient(OrgUsersController)
response = client.post(
"/users/123/", json={"username": "jane", "email": "[email protected]"}
)
assert response.status_code == 200
body = response.json()
assert body["org_id"] == 123
assert body["username"] == "jane"
assert "id" in body
Loading