fastbooks (part 2) - fastapi, pytest, heroku
Using pytest with FastAPI
In the last post we saw how to make a simple FastAPI app using the new sqlmodel to handle database conenction (and data validation). We also set up alembic to handle database migrations, which will come in handy when we deploy to Heroku later on. We'll start with integrating pytest
It's not necessary to have the same code as our project here, but it might make more sense if the project structure is similar:
.
├── Procfile
├── alembic
│ ├── README
│ ├── env.py
│ ├── script.py.mako
│ └── versions
│ ├── 0f599158e6f3_rename_acccess_token_to_hashed_password.py
│ ├── 41790a17a9dc_add_descrption_column.py
│ ├── 649a10b6cd01_.py
│ └── 6d7727a40b30_remove_access_token_column.py
├── alembic.ini
├── app
│ ├── __init__.py
│ ├── database.py
│ ├── main.py
│ ├── models.py
│ ├── routes
│ │ ├── books.py
│ │ └── users.py
│ ├── settings.py
│ ├── static
│ │ ├── css
│ │ │ ├── docs.css
│ │ │ └── theme.css
│ │ └── img
│ │ ├── books.png
│ │ ├── cloud.png
│ │ └── favicon.ico
│ └── templates
│ ├── book
│ │ ├── addbook.html
│ │ └── updatebook.html
│ ├── home
│ │ └── index.html
│ ├── shared
│ │ └── layout.html
│ └── users
│ └── login.html
├── bookdatabase.db
└── requirements.txt
The important things are:
- There is a directory
appwhich has all the code required to run the app, which is a python module (it has an__init__.pyfile)
We'll be using pytest to handle our tests, so make sure it's installed (pip install pytest if not). Make a folder
next to app called tests. This name is important because pytest will automatically look for a folder with this name to collect the tests.
FastAPI allows for really easy integration with pytest, by providing a TestClient. Make a file
called tests/test_main.py with the following content
import pytest
from fastapi import TestClient
from app.main import app
@pytest.fixture(name="client")
def testclient():
yield TestClient(app)
@pytest.fixture(name='session')
def session():
with Session(db) as session:
yield session
def get_session_override():
db_url = "sqlite:///"
def test_create_book(session):
book = Book(title="My first book about WORDS", author="Pytest McPyface")
session.add(book)
session.refresh(book)
assert book.id
assert book.title == "My first book about WORDS"
assert book.author == "Pytest McPyface"
def test_homepage(client):
homepage = client.get("/")
assert homepage.status_code == 200
assert "" != homepage.content
This file will define two basic tests to test the functionality of creating a book in the database and also that the homepage returns the correct status code. This can now be run with
pytest
Pytest Fixtures
The pytest fixtures that we've defined in test_main.py (that functions with the @pytest.fixture() decorators) are a very convenient way of defining things that are used in mulitple tests. We are using them here to define a session to connect to the database and a client to make our requests against. Note that we have also overridden the get_session dependency so that we aren't using the same database file for the tests as we are for developing.
The important thing is that pytest will take care of creating the fixtures and taking them down after each test (or each testing session, depending on the scope).
Deploying to Heroku
Create a Procfile in the base directory with the following content
web: gunicorn app.main:app -w 4 -k uvicornWorkers
on_start: alembic upgrade
