みんなに人気の Python テスト フレームワークである Pytest では、フィクスチャは、テストが開始される前に 何か を配置し、終了後にクリーンアップする再利用可能なコードです。たとえば、一時ファイルやフォルダー、環境のセットアップ、Web サーバーの起動などです。この投稿では、テスト データベース (空または既知の状態) を作成する Pytest フィクスチャの作成方法を見ていきます。クリーンアップされ、完全にクリーンなデータベース上で各テストを実行できるようになります.
目標
Psycopg 3 を使用して Pytest フィクスチャを作成し、テスト データベースを準備およびクリーンアップします。空のデータベースがテストに役立つことはほとんどないため、必要に応じて Yoyo 移行 (この記事の執筆時点では Web サイトがダウンしており、archive.org スナップショットに移動) を適用してデータベースを埋めます。
したがって、このブログ投稿で作成された test_db という名前の Pytest フィクスチャの要件は次のとおりです:
-
テスト前に存在する場合はテスト データベースを削除
- テストの前に空のデータベースを作成します
オプションで、テストの前に- 移行を適用するか、テスト データを作成します
- テスト データベースへの接続を提供します テスト
- テスト後にテスト データベースを削除 (失敗した場合でも)
テスト メソッドの引数にリストすることによってそれを要求するテスト メソッド:
def test_create_admin_table(test_db):
...
def test_create_admin_table(test_db):
...
テスト DB に接続された通常の Psycopg Connection インスタンスを受け取ります。テストは、単純な Psycopg の一般的な使用法と同様に、必要なことを何でも実行できます。例:
def test_create_admin_table(test_db):
# カーソルを開いてデータベース操作を実行します
cur = test_db.cursor()
# データを渡してクエリのプレースホルダーを埋め、Psycopg を実行させます
# 正しい変換 (SQL インジェクションなし!)
cur.execute(
"INSERT INTO テスト (数値, データ) 値 (%s, %s)",
(100, "abc'def"))
# データベースにクエリを実行し、Python オブジェクトとしてデータを取得します。
cur.execute("SELECT * FROM テスト")
cur.fetchone()
# (1, 100, "abc'def") が返されます
# `cur.fetchmany()`、`cur.fetchall()`を使用してリストを返すことができます
複数のレコードの数、またはカーソル上で反復することもできます
cur の記録用:
印刷(記録)
def test_create_admin_table(test_db):
...
動機と選択肢
データベースに依存するテストに PostgreSQL フィクスチャを約束する Pytest プラグインがいくつかあるようです。それらはあなたにとってうまくいくかもしれません。
同じことを約束するpytest-postgresqlを試しました。独自のフィクスチャを作成する前に試してみましたが、うまく機能させることができませんでした。おそらく、彼らのドキュメントが私にとって非常にわかりにくかったからでしょう。
もう 1 つの pytest-dbt-postgres は、まったく試していません。
プロジェクトファイルのレイアウト
古典的な Python プロジェクトでは、ソースは src/ にあり、テストは testing/:
にあります。
§── src
│ └── トゥヴォック
│ §── __init__.py
│ └── 販売
│ └── new_user.py
§── テスト
│ §── conftest.py
│ └── 販売
│ └── test_new_user.py
§── 要件.txt
━── yoyo.ini
def test_create_admin_table(test_db):
...
幻想的な Yoyo のような移行ライブラリを使用する場合、移行スクリプトは migrations/:
にある可能性があります。
§── 移行
§── 20240816_01_Yn3Ca-sales-user-user-add-last-run-table.py
§── ...
def test_create_admin_table(test_db):
...
構成
私たちのテスト DB フィクスチャには、非常に小さな構成が必要です:
- 接続 URL - (データベースなし)
- テスト データベース名 - テストごとに再作成されます
(オプション) - 移行フォルダー - すべてのテストに適用する移行スクリプト
Pytest には、複数のファイル間でフィクスチャを共有するための conftest.py という自然な場所があります。フィクスチャ設定もそこに移動します:
# DB 名なし!
TEST_DB_URL = "postgresql://localhost"
TEST_DB_NAME = "test_tuvok"
TEST_DB_MIGRATIONS_DIR = str(Path(__file__, "../../migrations").resolve())
def test_create_admin_table(test_db):
...
これらの値は環境変数から設定することも、状況に応じて設定することもできます。
test_db フィクスチャの作成
PostgreSQL と Psycopg ライブラリの知識を基に、conftest.py: にフィクスチャを作成します。
@pytest.fixture
def test_db():
# autocommit=True CREATE/DROP DATABASE のためトランザクションは開始されません
# トランザクションブロック内では実行できません。
psycopg.connect(TEST_DB_URL, autocommit=True) を conn として使用:
cur = conn.cursor()
# テスト DB を作成し、その前にドロップします
cur.execute(f'「{TEST_DB_NAME}」が存在する場合、(FORCE) を使用してデータベースを削除')
cur.execute(f'CREATE DATABASE "{TEST_DB_NAME}"')
# 作成したばかりのテスト DB に (新しい) 接続を返します
# 残念ながら、既存の Psycopg 接続のデータベースを直接変更することはできません。特定のデータベースへの接続が確立されると、そのデータベースに接続されます。
psycopg.connect(TEST_DB_URL, dbname=TEST_DB_NAME) を conn として使用:
収量コン
cur.execute(f'「{TEST_DB_NAME}」が存在する場合、(FORCE) を使用してデータベースを削除')
def test_create_admin_table(test_db):
...
移行フィクスチャの作成
私たちの場合は、
Yoyo マイグレーションを使用します。マイグレーションの適用を yoyo: という別のフィクスチャとして記述します。
@pytest.fixture
デフヨーヨー():
# `driver://user:pass@host:port/database_name?param=value` を期待してください。
# 渡された URL では次のことが必要です
URL = (
urlparse(TEST_DB_URL)
。
# 1) 使用するドライバ(スキーマ部分)を`postgresql psycopg`で変更
# psycopg 3 (`postgresql psycopg2` である 2 ではありません)
_replace(scheme="postgresql psycopg")
。
# 2) データベースをテスト データベースに変更します (移行が適用される)
_replace(パス=TEST_DB_NAME)
.geturl()
)
バックエンド = get_backend(url)
移行 = read_migrations(TEST_DB_MIGRATIONS_DIR)
len(移行) == 0の場合:
raise ValueError(f"「{TEST_DB_MIGRATIONS_DIR}」に Yoyo マイグレーションが見つかりません")
backend.lock() を使用:
backend.apply_migrations(backend.to_apply(移行))
def test_create_admin_table(test_db):
...
すべてのテスト データベースに移行を適用したい場合、test_db フィクスチャにヨーヨー フィクスチャが必要です:
@pytest.fixture
def test_db(ヨーヨー):
...
def test_create_admin_table(test_db):
...
一部のテストのみに移行を適用するには、ヨーヨーを個別に必要とします:
def test_create_admin_table(test_db, yoyo):
...
def test_create_admin_table(test_db):
...
結論
テストにクリーンなデータベースを提供する独自のフィクスチャを構築することは、私にとって Pytest と Postgres の両方をより深く掘り下げることができる貴重な経験でした。
この記事が独自のデータベース テスト スイートに役立つことを願っています。遠慮なくコメント欄に質問を残してください。コーディングを楽しんでください!