Pytest 및 PostgreSQL: 모든 테스트를 위한 새로운 데이터베이스

WBOY
풀어 주다: 2024-08-19 16:43:42
원래의
457명이 탐색했습니다.

Pytest and PostgreSQL: Fresh database for every test

모두가 선호하는 Python 테스트 프레임워크인 Pytest에서 픽스처는 테스트가 시작되기 전에 무언가를 정렬하고 종료 후에 정리하는 재사용 가능한 코드 조각입니다. 예를 들어, 임시 파일이나 폴더, 환경 설정, 웹 서버 시작 등이 있습니다. 이 게시물에서는 다음을 얻는 테스트 데이터베이스(비어 있거나 알려진 상태)를 생성하는 Pytest 픽스쳐를 생성하는 방법을 살펴보겠습니다. 정리하여 각 테스트를 완전히 깨끗한 데이터베이스에서 실행할 수 있습니다.

목표

테스트 데이터베이스를 준비하고 정리하기 위해 Psycopg 3을 사용하여 Pytest 픽스처를 생성하겠습니다. 빈 데이터베이스는 테스트에 거의 도움이 되지 않기 때문에 선택적으로 Yoyo 마이그레이션(웹사이트 작성 시 archive.org 스냅샷으로 이동)을 적용하여 채울 것입니다.

이 블로그 게시물에서 생성된 test_db라는 Pytest 픽스처에 대한 요구 사항은 다음과 같습니다.

  • 테스트 데이터베이스 삭제 테스트 이전에 존재하는 경우
  • 테스트 전에 빈 데이터베이스를 생성
  • 선택적으로 테스트 전에 마이그레이션을 적용하거나 테스트 데이터를 생성
  • 테스트 데이터베이스에 대한 연결 제공테스트
  • 드롭 테스트 데이터베이스 테스트 후(실패한 경우에도)

테스트 메서드 인수를 나열하여 요청하는 모든 테스트 메서드:

def test_create_admin_table(test_db):
    ...
로그인 후 복사

테스트 DB에 연결된 일반 Psycopg Connection 인스턴스를 받게 됩니다. 테스트는 일반 Psycopg 일반적인 사용법과 같이 필요한 모든 작업을 수행할 수 있습니다. 예:

def test_create_admin_table(test_db):
    # Open a cursor to perform database operations
    cur = test_db.cursor()

    # Pass data to fill a query placeholders and let Psycopg perform
    # the correct conversion (no SQL injections!)
    cur.execute(
        "INSERT INTO test (num, data) VALUES (%s, %s)",
        (100, "abc'def"))

    # Query the database and obtain data as Python objects.
    cur.execute("SELECT * FROM test")
    cur.fetchone()
    # will return (1, 100, "abc'def")

    # You can use `cur.fetchmany()`, `cur.fetchall()` to return a list
    # of several records, or even iterate on the cursor
    for record in cur:
        print(record)
로그인 후 복사

동기 부여 및 대안
데이터베이스에 의존하는 테스트를 위한 PostgreSQL 고정 장치를 약속하는 일부 Pytest 플러그인이 있는 것 같습니다. 그들은 당신에게 잘 작동할 수도 있습니다.

저는 동일한 결과를 약속하는 pytest-postgresql을 사용해 보았습니다. 나는 내 자신의 픽스처를 작성하기 전에 그것을 시도했지만 나에게 적합하게 만들 수 없었습니다. 아마도 그들의 문서가 나에게 매우 혼란스럽기 때문일 것입니다.

또 다른 pytest-dbt-postgres는 전혀 시도하지 않았습니다.


프로젝트 파일 레이아웃

클래식 Python 프로젝트에서 소스는 src/에 있고 테스트는 test/에 있습니다.

├── src
│   └── tuvok
│       ├── __init__.py
│       └── sales
│           └── new_user.py
├── tests
│   ├── conftest.py
│   └── sales
│       └── test_new_user.py
├── requirements.txt
└── yoyo.ini
로그인 후 복사

환상적인 Yoyo와 같은 마이그레이션 라이브러리를 사용하는 경우 마이그레이션 스크립트가 migrations/:
에 있을 가능성이 높습니다.

├── migrations
    ├── 20240816_01_Yn3Ca-sales-user-user-add-last-run-table.py
    ├── ...
로그인 후 복사

구성

테스트 DB 고정 장치에는 약간의 구성이 필요합니다.

  • 연결 URL - (데이터베이스 없음)
  • 테스트 데이터베이스 이름 - 모든 테스트마다 다시 생성됩니다
  • (선택 사항) 마이그레이션 폴더 - 모든 테스트에 적용할 마이그레이션 스크립트

Pytest에는 여러 파일에서 픽스처를 공유할 수 있는 자연스러운 장소인 conftest.py가 있습니다. 고정 장치 구성도 거기에 적용됩니다:

# Without DB name!
TEST_DB_URL = "postgresql://localhost"
TEST_DB_NAME = "test_tuvok"
TEST_DB_MIGRATIONS_DIR = str(Path(__file__, "../../migrations").resolve())
로그인 후 복사

이러한 값은 환경 변수 또는 상황에 맞게 설정할 수 있습니다.

test_db 고정 장치 생성

PostgreSQL 및 Psycopg 라이브러리에 대한 지식을 바탕으로 conftest.py에 픽스처를 작성합니다.

@pytest.fixture
def test_db():
    # autocommit=True start no transaction because CREATE/DROP DATABASE
    # cannot be executed in a transaction block.
    with psycopg.connect(TEST_DB_URL, autocommit=True) as conn:
        cur = conn.cursor()

        # create test DB, drop before
        cur.execute(f'DROP DATABASE IF EXISTS "{TEST_DB_NAME}" WITH (FORCE)')
        cur.execute(f'CREATE DATABASE "{TEST_DB_NAME}"')

        # Return (a new) connection to just created test DB
        # Unfortunately, you cannot directly change the database for an existing Psycopg connection. Once a connection is established to a specific database, it's tied to that database.
        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)')
로그인 후 복사

마이그레이션 픽스쳐 생성

저희 경우에는 Yoyo 마이그레이션을 사용합니다. yoyo라는 또 다른 픽스처로 Apply migration을 작성하세요.

@pytest.fixture
def yoyo():
    # Yoyo expect `driver://user:pass@host:port/database_name?param=value`.
    # In passed URL we need to
    url = (
        urlparse(TEST_DB_URL)
        .
        # 1) Change driver (schema part) with `postgresql+psycopg` to use
        # psycopg 3 (not 2 which is `postgresql+psycopg2`)
        _replace(scheme="postgresql+psycopg")
        .
        # 2) Change database to test db (in which migrations will apply)
        _replace(path=TEST_DB_NAME)
        .geturl()
    )

    backend = get_backend(url)
    migrations = read_migrations(TEST_DB_MIGRATIONS_DIR)

    if len(migrations) == 0:
        raise ValueError(f"No Yoyo migrations found in '{TEST_DB_MIGRATIONS_DIR}'")

    with backend.lock():
        backend.apply_migrations(backend.to_apply(migrations))
로그인 후 복사

모든 테스트 데이터베이스에 마이그레이션을 적용하려면 test_db 고정 장치에 yoyo 고정 장치가 필요합니다.

@pytest.fixture
def test_db(yoyo):
    ...
로그인 후 복사

일부 테스트에만 마이그레이션을 적용하려면 yoyo를 개별적으로 요구하세요.

def test_create_admin_table(test_db, yoyo):
    ...
로그인 후 복사

결론

테스트에 깨끗한 데이터베이스를 제공하기 위해 자체 픽스처를 구축하는 것은 Pytest와 Postgres에 대해 더 깊이 탐구할 수 있는 보람 있는 경험이었습니다.

이 기사가 귀하의 데이터베이스 테스트 스위트에 도움이 되었기를 바랍니다. 궁금한 점은 댓글로 남겨주시고 행복한 코딩하세요!

위 내용은 Pytest 및 PostgreSQL: 모든 테스트를 위한 새로운 데이터베이스의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!