Pytest と PostgreSQL: すべてのテストのための最新のデータベース (パート II)

WBOY
リリース: 2024-09-03 16:09:19
オリジナル
840 人が閲覧しました

Pytest and PostgreSQL: Fresh database for every test (part II)

前の投稿では、テスト メソッドの前後に 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 治具工場

「静的」フィクスチャはここでは少し制限があります。わずかな違いだけでほぼ同じものが必要な場合は、コードを複製する必要があります。 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 は、ジェネレーター関数の実行を一時停止し、呼び出し元に単一の値を返すために使用されます。
  • while 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 はありません)、

  • ファクトリ フィクスチャは内部関数を返します (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 サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!