Setting Up SQLAlchemy + mypy + pytest
TL;DR
SQLAlchemy is an excellent ORM, but unlike django and similar frameworks, you need to set up testing and migrations yourself.
There are various tools available, but this time we will set up the environment with the following stack. We also use mypy for type checking since types are always welcome.
- migration -> alembic
- test -> pytest
The actual steps are:
- Set up the test environment
- Create
UserandPostmodels - Test
- Migration
Install
Use your preferred tool. Recently I've been using poetry.
Add the mypy SQLAlchemy plugins to pyproject.toml.
The result should look like this:
pyproject.toml
Database Setup
Set things up so we can perform migrations.
Also prepare the database. We use postgresql. If you want persistence, uncomment the volume section.
docker-compose.yaml
Directory Structure
We use the following directory structure. The models directory is set up to allow splitting its contents.
1. Setting Up the Test Environment
First, place globally used items (DB URL retrieval, DeclarativeMeta, session query counter class) in models/base.py.
The query counter is mainly used in tests, so it might be better placed elsewhere.
models/base.py
In conftest.py, write fixtures that create a test database and test sessions.
To make testing convenient, we allow overriding the test database settings via command-line options.
- The test database is initialized per pytest session.
- Test sessions are initialized per pytest function.
Also, when relations exist, drop_all may not work properly, so we disable constraints with SET CONSTRAINTS ALL DEFERRED;.
For MySQL, use SET FOREIGN_KEY_CHECKS=0; instead.
tests/conftest.py
2. Model Creation
We assume a standard blog where a User has multiple Posts.
Type inference does not work for dialects and relations, so they need explicit type annotations. Also, UUID(as_uuid=True) must be set for things to work correctly, so I mechanically add it for now.
models/blog.py
3. Test
You can test as follows. Add --echo if you want to see details.
tests/test_blog.py
Let's also verify the database contents.
4. Migration
Now that the tests passed, let's run migration to reflect the changes in the main DB.
Edit the migrations/env.py generated by alembic.
You must import all models that inherit from Base.
migrations/env.py
Run the migration with alembic.
Verify that the results are reflected in the database.
Conclusion
That completes my personal setup for a usable SQLAlchemy test environment. If you have suggestions for improvement, please let me know via issues on the following repository.