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

✅ Add the docs_src directory to test coverage and update tests #1904

Merged
merged 14 commits into from
Jul 29, 2021
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
6 changes: 3 additions & 3 deletions docs/en/docs/advanced/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ And then we can require it from the *path operation function* as a dependency an

Then it would be very easy to provide a different settings object during testing by creating a dependency override for `get_settings`:

```Python hl_lines="8-9 12 21"
```Python hl_lines="9-10 13 21"
{!../../../docs_src/settings/app02/test_main.py!}
```

Expand Down Expand Up @@ -288,7 +288,7 @@ Reading a file from disk is normally a costly (slow) operation, so you probably
But every time we do:

```Python
config.Settings()
Settings()
```

a new `Settings` object would be created, and at creation it would read the `.env` file again.
Expand All @@ -297,7 +297,7 @@ If the dependency function was just like:

```Python
def get_settings():
return config.Settings()
return Settings()
```

we would create that object for each request, and we would be reading the `.env` file for each request. ⚠️
Expand Down
8 changes: 4 additions & 4 deletions docs_src/settings/app01/main.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from fastapi import FastAPI

from . import config
from .config import settings

app = FastAPI()


@app.get("/info")
async def info():
return {
"app_name": config.settings.app_name,
"admin_email": config.settings.admin_email,
"items_per_user": config.settings.items_per_user,
"app_name": settings.app_name,
"admin_email": settings.admin_email,
"items_per_user": settings.items_per_user,
}
6 changes: 3 additions & 3 deletions docs_src/settings/app02/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

from fastapi import Depends, FastAPI

from . import config
from .config import Settings

app = FastAPI()


@lru_cache()
def get_settings():
return config.Settings()
return Settings()


@app.get("/info")
async def info(settings: config.Settings = Depends(get_settings)):
async def info(settings: Settings = Depends(get_settings)):
return {
"app_name": settings.app_name,
"admin_email": settings.admin_email,
Expand Down
10 changes: 5 additions & 5 deletions docs_src/settings/app02/test_main.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
from fastapi.testclient import TestClient

from . import config, main
from .config import Settings
from .main import app, get_settings

client = TestClient(main.app)
client = TestClient(app)


def get_settings_override():
return config.Settings(admin_email="testing_admin@example.com")
return Settings(admin_email="testing_admin@example.com")


main.app.dependency_overrides[main.get_settings] = get_settings_override
app.dependency_overrides[get_settings] = get_settings_override


def test_app():

response = client.get("/info")
data = response.json()
assert data == {
Expand Down
2 changes: 1 addition & 1 deletion scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ bash ./scripts/lint.sh
# Check README.md is up to date
python ./scripts/docs.py verify-readme
export PYTHONPATH=./docs_src
pytest --cov=fastapi --cov=tests --cov=docs/src --cov-report=term-missing --cov-report=xml tests ${@}
pytest --cov=fastapi --cov=tests --cov=docs_src --cov-report=term-missing:skip-covered --cov-report=xml tests ${@}
12 changes: 12 additions & 0 deletions tests/test_tutorial/test_bigger_applications/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,12 @@
("/users/foo", 422, no_jessica, {}),
("/users/me?token=jessica", 200, {"username": "fakecurrentuser"}, {}),
("/users/me", 422, no_jessica, {}),
(
"/users?token=monica",
400,
{"detail": "No Jessica token provided"},
{},
),
(
"/items?token=jessica",
200,
Expand All @@ -372,6 +378,12 @@
{"name": "Plumbus", "item_id": "plumbus"},
{"X-Token": "fake-super-secret-token"},
),
(
"/items/bar?token=jessica",
404,
{"detail": "Item not found"},
{"X-Token": "fake-super-secret-token"},
),
("/items/plumbus", 422, no_jessica, {"X-Token": "fake-super-secret-token"}),
(
"/items?token=jessica",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,10 @@ def test_disable_openapi(monkeypatch):
assert response.status_code == 404, response.text
response = client.get("/redoc")
assert response.status_code == 404, response.text


def test_root():
client = TestClient(tutorial001.app)
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
10 changes: 9 additions & 1 deletion tests/test_tutorial/test_settings/test_app02.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
from fastapi.testclient import TestClient
from pytest import MonkeyPatch

from docs_src.settings.app02 import main, test_main

client = TestClient(main.app)


def test_setting_override():
def test_settings(monkeypatch: MonkeyPatch):
monkeypatch.setenv("ADMIN_EMAIL", "admin@example.com")
settings = main.get_settings()
assert settings.app_name == "Awesome API"
assert settings.items_per_user == 50


def test_override_settings():
test_main.test_app()
10 changes: 10 additions & 0 deletions tests/test_tutorial/test_testing/test_main_b.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from docs_src.app_testing import test_main_b


def test_app():
test_main_b.test_create_existing_item()
test_main_b.test_create_item()
test_main_b.test_create_item_bad_token()
test_main_b.test_read_inexistent_item()
test_main_b.test_read_item()
test_main_b.test_read_item_bad_token()
Copy link
Sponsor Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't duplicate the tests as well?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remember correctly, the tests from docs_src are not run by pytest, so the way to run them and have coverage for that is to do this.

Sorry for the long delay! 🙈 I wanted to personally address each issue/PR and they piled up through time, but now I'm checking each one in order.

Copy link
Sponsor Collaborator Author

@Kludex Kludex Nov 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember doing what I did because before my changes the tests were running twice, and I think (I didn't check) this makes them run twice (those were your changes, just for context...), because (I think) the import is enough to make pytest find the tests. Anyway... 👀

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, maybe I have to review this again.

Although, one possible explanation is if you were running pytest and I was running pytest tests.

But anyway, yeah, maybe for later. 😅

7 changes: 6 additions & 1 deletion tests/test_tutorial/test_websockets/test_tutorial003.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from fastapi.testclient import TestClient

from docs_src.websockets.tutorial003 import app
from docs_src.websockets.tutorial003 import app, html

client = TestClient(app)


def test_get():
response = client.get("/")
assert response.text == html


def test_websocket_handle_disconnection():
with client.websocket_connect("/ws/1234") as connection, client.websocket_connect(
"/ws/5678"
Expand Down