前の投稿では、テスト メソッドの前後に Postgres データベースを作成/ドロップする Pytest フィクスチャを作成しました。このパートでは、Pytest ファクトリー フィクスチャを使用して、フィクスチャをより柔軟で構成可能なものに改善します。
たとえば、テストでモックするデータベースが複数ある場合
def test_create_user(test_db1, test_db2): ...
ほぼ 2 つの同一のフィクスチャを作成する必要があります:
TEST_DB_URL = "postgresql://localhost" TEST_DB1_NAME = "test_foo" TEST_DB2_NAME = "test_bar" @pytest.fixture def test_db1(): with psycopg.connect(TEST_DB_URL, autocommit=True) as conn: cur = conn.cursor() cur.execute(f'DROP DATABASE IF EXISTS "{TEST_DB1_NAME}" WITH (FORCE)') cur.execute(f'CREATE DATABASE "{TEST_DB1_NAME}"') with psycopg.connect(TEST_DB_URL, dbname=TEST_DB1_NAME) as conn: yield conn cur.execute(f'DROP DATABASE IF EXISTS "{TEST_DB1_NAME}" WITH (FORCE)') @pytest.fixture def test_db2(): with psycopg.connect(TEST_DB_URL, autocommit=True) as conn: cur = conn.cursor() cur.execute(f'DROP DATABASE IF EXISTS "{TEST_DB2_NAME}" WITH (FORCE)') cur.execute(f'CREATE DATABASE "{TEST_DB2_NAME}"') with psycopg.connect(TEST_DB_URL, dbname=TEST_DB2_NAME) as conn: yield conn cur.execute(f'DROP DATABASE IF EXISTS "{TEST_DB2_NAME}" WITH (FORCE)')
「静的」フィクスチャはここでは少し制限があります。わずかな違いだけでほぼ同じものが必要な場合は、コードを複製する必要があります。 Pytest に設備としての工場という概念があることを願っています。
ファクトリー フィクスチャは、別のフィクスチャを返すフィクスチャです。 すべてのファクトリと同様、これは関数であるため、返されたフィクスチャをカスタマイズするための引数を受け取ることができます。慣例により、make_test_db のように、先頭に make_* を付けることができます。
フィクスチャ ファクトリ make_test_db への唯一の引数は、作成/削除するテスト データベース名です。
それでは、make_test_db ファクトリ フィクスチャに基づいて 2 つの「特殊な」フィクスチャを作成しましょう。
使用法は次のようになります:
@pytest.fixture def test_db_foo(make_test_db): yield from make_test_db("test_foo") @pytest.fixture def test_db_bar(make_test_db): yield from make_test_db("test_bar")
からの収量に気づきましたか? yield と yield の間には、ジェネレーター内でデータの流れと制御をどのように処理するかという重要な違いがあります。
Python では、yield と yield from の両方がジェネレーター関数内で一連の値を生成するために使用されますが、
つまり、私たちは特殊な治具からではなく、治具工場から「譲り受け」たいと考えています。したがって、ここでは yield from が必要です。
元のフィクスチャ作成/削除データベースに必要な変更は、コードを内部関数にラップすることを除いて、実際にはほとんどありません。
@pytest.fixture def make_test_db(): def _(test_db_name: str): with psycopg.connect(TEST_DB_URL, autocommit=True) as conn: cur = conn.cursor() cur.execute(f'DROP DATABASE IF EXISTS "{test_db_name}" WITH (FORCE)') # type: ignore cur.execute(f'CREATE DATABASE "{test_db_name}"') # type: ignore with psycopg.connect(TEST_DB_URL, dbname=test_db_name) as conn: yield conn cur.execute(f'DROP DATABASE IF EXISTS "{test_db_name}" WITH (FORCE)') # type: ignore yield _
前のパートでは、作成したばかりの空のデータベースに Yoyo マイグレーションを適用するフィクスチャもありました。あまり柔軟性もありませんでした。同じことをして、実際のコードを内部関数にラップしてみましょう。
この場合、コードはテスト メソッドから戻った後にクリーンアップを行う必要がないため (yield はありません)、
@pytest.fixture def make_yoyo(): """Applies Yoyo migrations to test DB.""" def _(test_db_name: str, migrations_dir: str): url = ( urlparse(TEST_DB_URL) . _replace(scheme="postgresql+psycopg") . _replace(path=test_db_name) .geturl() ) backend = get_backend(url) migrations = read_migrations(migrations_dir) if len(migrations) == 0: raise ValueError(f"No Yoyo migrations found in '{migrations_dir}'") with backend.lock(): backend.apply_migrations(backend.to_apply(migrations)) return _ @pytest.fixture def yoyo_foo(make_yoyo): migrations_dir = str(Path(__file__, "../../foo/migrations").resolve()) make_yoyo("test_foo", migrations_dir) @pytest.fixture def yoyo_bar(make_yoyo): migrations_dir = str(Path(__file__, "../../bar/migrations").resolve()) make_yoyo("test_bar", migrations_dir)
2 つのデータベースが必要で、それらに移行を適用するテスト方法:
from psycopg import Connection def test_get_new_users_since_last_run( test_db_foo: Connection, test_db_bar: Connection, yoyo_foo, yoyo_bar): test_db_foo.execute("...") ...
Pytest メソッドのデータベースを作成および削除する独自のフィクスチャ ファクトリを構築することは、実際には、Python ジェネレーターと演算子からの利回り/利回りを練習する良い練習になります。
この記事が独自のデータベース テスト スイートに役立つことを願っています。お気軽にコメント欄に質問を残していただき、コーディングを楽しんでください!
以上がPytest と PostgreSQL: すべてのテストのための最新のデータベース (パート II)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。