- https://www.sqlalchemy.org/ This is to handle the database
- https://docs.pydantic.dev/latest/ this is to handle the validation json serialization and deserialization>
- https://fastapi.tiangolo.com/ This is the api
- https://alembic.sqlalchemy.org/en/latest/index.html The database migrations
Install Instructions
- git clone git@github.com:kaw393939/event_manager.git
- cd ./event_manager
- docker compose up --build -d
- docker compose exec fastapi alembic upgrade head
- get pgadmin setup at http://localhost:5050
- Ypu must add the server and use the settings in the ./docker-compose.yml file. See Video for Setup
- Verify that you have the alembic and user table in the public->schemas->tables menu in pgadmin after the server is added
- See video for how to view the records
- docker compose exec fastap pytest (see video for variations for this command)
NOTE When you run pytest it will drop the tables in the database. This will cause the docs page to malfunction because it won't have tables. To fix this you need to manually drop the alembic table using pgadmin and then run the migration again with the command you used to install. This is why you should use the tests first and not need to go into the UI to manually do it. So i save doing the API testing on the docs page until last to find edge cases or any issues I didn't account for.
Important Links
- http://localhost/docs
- http://localhost:5050
- Login
- Username: admin@example.com
- Password: adminpassword
- Login
- docker compose up --build
- docker compose exec fastapi pytest
- docker compose exec fastapi pytest tests/test_services/test_user_service.py::test_list_users
- Need to apply database migrationss: docker compose exec fastapi alembic upgrade head
- Creating database migration: docker compose exec fastapi alembic revision --autogenerate -m 'added admin'
When testing applications that interact with databases, it's crucial to ensure the integrity and reliability of the data. Database testing involves verifying that the application can correctly communicate with the database, perform the necessary operations, and handle data consistency and accuracy.
In this code, we will explore how to set up a testing environment for a FastAPI application that uses an asynchronous database connection. We will cover the concepts of fixtures, database initialization, session management, and the use of asynchronous programming with SQLAlchemy and FastAPI testing.
Asynchronous programming is a programming paradigm that allows multiple tasks to be executed concurrently without blocking the main execution flow. In the context of web applications, asynchronous programming is particularly useful for handling long-running operations, such as database queries or external API requests, without blocking the server's ability to handle other incoming requests.
In Python, asynchronous programming is achieved using the asyncio library and the async/await syntax. Functions that are defined with the async def keyword are called coroutines, and they can be paused and resumed during execution, allowing other tasks to be executed in the meantime.
In this code, we utilize asynchronous programming to efficiently interact with the database using SQLAlchemy's asynchronous extensions. We create an asynchronous engine, session maker, and scoped session to manage the database connection and perform database operations asynchronously.
To set up a testing environment for a FastAPI application with a database, we need to consider several aspects:
- We retrieve the database configuration settings using the
get_settings()function. - We modify the database URL to use the
postgresql+asyncpg://scheme for asynchronous communication with the database. - We create an asynchronous engine using
create_async_enginewith the modified database URL. - We define
AsyncTestingSessionLocalas a session maker for creating asynchronous sessions. - We create a scoped session
AsyncSessionScopedusing theAsyncTestingSessionLocalsession maker.
Fixtures are reusable objects that are created and managed by the testing framework (pytest in this case).
They provide a way to set up the necessary objects and dependencies for testing, such as database connections, test clients, and test data.
Fixtures are defined using the @pytest.fixture decorator and can have different scopes (e.g., function, module, session) to control their lifecycle.
- This fixture creates an asynchronous test client using
AsyncClientfromhttpx. - It allows us to make HTTP requests to the FastAPI application during testing.
- It overrides the
get_async_dbdependency with a lambda function that returns the database session. - The fixture yields the client for use in tests and clears the dependency overrides after the test.
- This fixture generates an access token by making a POST request to the
/tokenendpoint with test credentials. - It returns the access token for use in authenticated requests during testing.
- This fixture initializes the database by calling
initialize_async_dbwith the database URL. - It has a session scope and is automatically used in all tests.
- This fixture sets up the database before each test function.
- It creates all the database tables using
Base.metadata.create_all. - After the test, it drops all the tables using
Base.metadata.drop_alland disposes of the database engine. - This ensures that each test starts with a clean database state.
- This fixture creates an asynchronous database session using
AsyncSessionScoped. - It yields the session for use in tests and closes the session after the test.
- It allows us to interact with the database during testing.
- This fixture creates a single User object with fake data using the Faker instance.
- It adds the user to the database session and commits the changes.
- It returns the created user object for use in tests.
- This fixture creates 50 User objects with the same role and fake data.
- It adds each user to the database session and commits the changes.
- It returns the list of created users for use in tests.
With the testing setup in place, we can now write tests to verify the behavior of our FastAPI application and its interaction with the database. Here are some common testing procedures:
- Test database connectivity:
- Ensure that the application can successfully connect to the database.
- Verify that the database session is properly created and managed.
- Test database queries:
- Write tests to check that the application can execute database queries correctly.
- Verify that the expected data is retrieved from the database based on the query parameters.
- Test edge cases and error scenarios to ensure proper handling of invalid or missing data.
- Test data integrity:
- Write tests to verify that the application maintains data integrity when performing database operations.
- Check that data is properly inserted, updated, and deleted in the database.
- Validate that the application enforces any constraints or business rules related to the data.
- Test asynchronous behavior:
- Write tests to verify that the application handles asynchronous database operations correctly.
- Ensure that the application can handle concurrent requests and manage the asynchronous flow of execution.
- Test scenarios where multiple asynchronous operations are performed simultaneously to check for proper synchronization and data consistency.
- Test error handling:
- Write tests to verify that the application handles database errors and exceptions gracefully.
- Check that appropriate error messages and status codes are returned when database operations fail.
- Verify that the application can recover from database errors and maintain a stable state.
By following these testing procedures and utilizing the provided testing setup, we can ensure that our FastAPI application interacts with the database correctly, handles asynchronous operations efficiently, and maintains data integrity.
Testing database interactions in a FastAPI application requires careful setup and consideration of asynchronous programming concepts. By leveraging fixtures, database initialization, session management, and asynchronous programming with SQLAlchemy and FastAPI testing, we can create a robust testing environment that allows us to verify the correctness and reliability of our application's database operations.