跳转到主要内容

Pytest的Postgresql fixture和fixture工厂。

项目描述

https://raw.githubusercontent.com/ClearcodeHQ/pytest-postgresql/master/logo.png

pytest-postgresql

Latest PyPI version Wheel Status Supported Python Versions License

这是什么?

这是一个pytest插件,它允许您测试依赖于运行中的PostgreSQL数据库的代码。它允许您为PostgreSQL进程和客户端指定fixture。

如何使用

使用以下方式安装

pip install pytest-postgresql

您还需要安装 psycopg。请参阅其安装说明。请注意,此插件需要psycopg版本3。对于需要后者(请参阅这些说明)的库,可以同时安装版本3和版本2。

插件包含三个fixture

  • postgresql - 它是一个具有功能范围的客户端配置。每次测试后,它都会结束所有剩余的连接,并从PostgreSQL中删除测试数据库,确保可重复性。此配置返回已经连接的psycopg连接。

  • postgresql_proc - 会话范围配置,首次使用时启动PostgreSQL实例,在测试结束时停止。

  • postgresql_noproc - 一个无进程配置,连接到已经运行的PostgreSQL实例。例如,在Docker化的测试环境中,或CI提供PostgreSQL服务。

只需将其中之一包含到您的测试配置文件列表中。

如果您需要,也可以创建额外的PostgreSQL客户端和进程配置。

from pytest_postgresql import factories

postgresql_my_proc = factories.postgresql_proc(
    port=None, unixsocketdir='/var/run')
postgresql_my = factories.postgresql('postgresql_my_proc')

示例测试

def test_example_postgres(postgresql):
    """Check main postgresql fixture."""
    cur = postgresql.cursor()
    cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")
    postgresql.commit()
    cur.close()

如果您想自动将模式填充到数据库配置中,有两种方法

  1. 客户端配置特定

  2. 进程配置特定

两者都接受相同的可能加载器集合

  • SQL文件路径

  • 加载函数导入路径(字符串)

  • 实际的加载函数

该函数将接收 hostportuserdbnamepassword 关键字参数,并需要在内部执行数据库连接。但是,您将能够运行SQL文件甚至触发编程数据库迁移。

客户端特定每次测试都会加载数据库

from pathlib import Path
postgresql_my_with_schema = factories.postgresql(
    'postgresql_my_proc',
    load=[Path("schemafile.sql"), Path("otherschema.sql"), "import.path.to.function", "import.path.to:otherfunction", load_this]
)

进程配置在每个测试会话中只加载一次,并将数据加载到模板数据库中。然后,客户端配置在每个测试中从模板数据库中创建测试数据库,这可以显著 加快测试速度

from pathlib import Path
postgresql_my_proc = factories.postgresql_proc(
    load=[Path("schemafile.sql"), Path("otherschema.sql"), "import.path.to.function", "import.path.to:otherfunction", load_this]
)
pytest --postgresql-populate-template=path.to.loading_function --postgresql-populate-template=path.to.other:loading_function --postgresql-populate-template=path/to/file.sql

示例中的loading_function将接收,并需要提交。

连接到现有的PostgreSQL数据库

一些项目正在使用已经运行的PostgreSQL服务器(例如在Docker实例上)。为了连接到它们,人们将使用 postgresql_noproc 配置。

postgresql_external = factories.postgresql('postgresql_noproc')

默认情况下,postgresql_noproc 配置将连接到使用 5432 端口的PostgreSQL实例。标准配置选项适用于它。

这些是适用于所有级别与 postgresql_noproc 配置的配置选项

配置

您可以通过三种方式定义设置,它是配置工厂参数、命令行选项和pytest.ini配置选项。您可以选择您喜欢的,但请记住,这些设置按照以下顺序处理

  • 配置工厂参数

  • 命令行选项

  • pytest.ini文件中的配置选项

配置选项

PostgreSQL选项

配置工厂参数

命令行选项

pytest.ini选项

无操作进程配置

默认

可执行文件路径

可执行文件

–postgresql-exec

postgresql_exec

/usr/lib/postgresql/13/bin/pg_ctl

host

host

–postgresql-host

postgresql_host

yes

127.0.0.1

port

port

–postgresql-port

postgresql_port

yes (5432)

random

postgresql用户

user

–postgresql-user

postgresql_user

yes

postgres

password

password

–postgresql-password

postgresql_password

yes

启动参数(额外的pg_ctl参数)

startparams

–postgresql-startparams

postgresql_startparams

-w

Postgres可执行文件额外参数(通过pg_ctl的-o参数传递)

postgres_options

–postgresql-postgres-options

postgresql_postgres_options

Unixsocket位置

unixsocket

–postgresql-unixsocketdir

postgresql_unixsocketdir

$TMPDIR

由配置文件创建的数据库名称

dbname

–postgresql-dbname

postgresql_dbname

是的,但是使用xdist时,会向名称中添加索引,因此每个工作进程的结果是test0、test1。

测试

默认模式,可以是sql文件或导入路径到将加载它的函数(每个的值列表)

加载

–postgresql-load

postgresql_load

yes

PostgreSQL连接选项

选项

–postgresql-options

postgresql_options

yes

示例用法

  • 将其作为参数传递到您自己的 fixture 中

    postgresql_proc = factories.postgresql_proc(
        port=8888)
  • 在运行测试时,使用 --postgresql-port 命令行选项

    py.test tests --postgresql-port=8888
  • 在您的 pytest.ini 文件中将端口指定为 postgresql_port

    为此,在 pytest.ini[pytest] 部分下添加如下一行

    [pytest]
    postgresql_port = 8888

示例

为测试填充数据库

使用 SQLAlchemy

此示例演示了如何填充数据库并创建 SQLAlchemy 的 ORM 连接

以下示例是从 pyramid_fullauth 测试中简化后的 session fixture

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.pool import NullPool
from zope.sqlalchemy import register


@pytest.fixture
def db_session(postgresql):
    """Session for SQLAlchemy."""
    from pyramid_fullauth.models import Base

    connection = f'postgresql+psycopg2://{postgresql.info.user}:@{postgresql.info.host}:{postgresql.info.port}/{postgresql.info.dbname}'

    engine = create_engine(connection, echo=False, poolclass=NullPool)
    pyramid_basemodel.Session = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
    pyramid_basemodel.bind_engine(
        engine, pyramid_basemodel.Session, should_create=True, should_drop=True)

    yield pyramid_basemodel.Session

    transaction.commit()
    Base.metadata.drop_all(engine)


@pytest.fixture
def user(db_session):
    """Test user fixture."""
    from pyramid_fullauth.models import User
    from tests.tools import DEFAULT_USER

    new_user = User(**DEFAULT_USER)
    db_session.add(new_user)
    transaction.commit()
    return new_user


def test_remove_last_admin(db_session, user):
    """
    Sample test checks internal login, but shows usage in tests with SQLAlchemy
    """
    user = db_session.merge(user)
    user.is_admin = True
    transaction.commit()
    user = db_session.merge(user)

    with pytest.raises(AttributeError):
        user.is_admin = False

在 fixtures 之外维护数据库状态

使用 pytest-postgresql 数据库管理功能,维护数据库状态是可能的,并且似乎在测试的库中使用

为此导入 DatabaseJanitor 并使用其 init 和 drop 方法

import pytest
from pytest_postgresql.janitor import DatabaseJanitor

@pytest.fixture
def database(postgresql_proc):
    # variable definition

    janitor = DatabaseJanitor(
        postgresql_proc.user,
        postgresql_proc.host,
        postgresql_proc.port,
        "my_test_database",
        postgresql_proc.version,
        password="secret_password",
    )
    janitor.init()
    yield psycopg2.connect(
        dbname="my_test_database",
        user=postgresql_proc.user,
        password="secret_password",
        host=postgresql_proc.host,
        port=postgresql_proc.port,
    )
    janitor.drop()

或将其用作上下文管理器

import pytest
from pytest_postgresql.janitor import DatabaseJanitor

@pytest.fixture
def database(postgresql_proc):
    # variable definition

    with DatabaseJanitor(
        postgresql_proc.user,
        postgresql_proc.host,
        postgresql_proc.port,
        "my_test_database",
        postgresql_proc.version,
        password="secret_password",
    ):
        yield psycopg2.connect(
            dbname="my_test_database",
            user=postgresql_proc.user,
            password="secret_password",
            host=postgresql_proc.host,
            port=postgresql_proc.port,
        )

连接到 Postgresql(在 docker 中)

要连接到运行在 docker 中的 postgresql 并在上面运行测试,请使用 noproc fixtures。

docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres

这将启动在 docker 容器中的 postgresql,但是使用本地安装的 postgresql 并没有太大区别。

在测试中,请确保所有测试都使用 postgresql_noproc fixture,如下所示

from pytest_postgresql import factories


postgresql_in_docker = factories.postgresql_noproc()
postgresql = factories.postgresql("postgresql_in_docker", dbname="test")


def test_postgres_docker(postgresql):
    """Run test."""
    cur = postgresql.cursor()
    cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")
    postgresql.commit()
    cur.close()

然后运行测试

pytest --postgresql-host=172.17.0.2 --postgresql-password=mysecretpassword

所有测试的基本数据库状态

如果您有多个需要共同初始化的测试,您可以定义一个 load 并将其传递到您的自定义 postgresql 进程 fixture

import pytest_postgresql.factories
def load_database(**kwargs):
    db_connection: connection = psycopg2.connect(**kwargs)
    with db_connection.cursor() as cur:
        cur.execute("CREATE TABLE stories (id serial PRIMARY KEY, name varchar);")
        cur.execute(
            "INSERT INTO stories (name) VALUES"
            "('Silmarillion'), ('Star Wars'), ('The Expanse'), ('Battlestar Galactica')"
        )
        db_connection.commit()

postgresql_proc = factories.postgresql_proc(
    load=[load_database],
)

postgresql = factories.postgresql(
    "postgresql_proc",
)

此过程将是进程 fixture 填充模板数据库,然后客户端 fixture 将自动使用它来从头开始创建测试数据库。快速、干净且没有悬空事务,这些事务可能被意外回滚。

相同的做法将适用于 noproces fixture,无论连接到的是在 docker 机器上运行还是远程或本地运行的已运行的 postgresql 实例。

使用 SQLAlchemy 初始化基本数据库状态

如何使用 SQLAlchemy 进行公共初始化

def load_database(**kwargs):
    connection = f"postgresql+psycopg2://{kwargs['user']}:@{kwargs['host']}:{kwargs['port']}/{kwargs['dbname']}"
    engine = create_engine(connection)
    Base.metadata.create_all(engine)
    session = scoped_session(sessionmaker(bind=engine))
    # add things to session
    session.commit()

postgresql_proc = factories.postgresql_proc(load=[load_database])

postgresql = factories.postgresql('postgresql_proc') # still need to check if this is actually needed or not

@pytest.fixture
def dbsession(postgresql):
    connection = f'postgresql+psycopg2://{postgresql.info.user}:@{postgresql.info.host}:{postgresql.info.port}/{postgresql.info.dbname}'
    engine = create_engine(connection)

    session = scoped_session(sessionmaker(bind=engine))

    yield session
    # 'Base.metadata.drop_all(engine)' here specifically does not work. It is also not needed. If you leave out the session.close()
    # all the tests still run, but you get a warning/error at the end of the tests.
    session.close()

发布

首先安装 pipenv 和 –dev 依赖项,然后运行

pipenv run tbump [NEW_VERSION]

项目详细信息


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分布

pytest_postgresql-6.1.1.tar.gz (47.5 kB 查看哈希值)

上传时间

构建分布

pytest_postgresql-6.1.1-py3-none-any.whl (40.5 kB 查看哈希值)

上传时间 Python 3

由以下机构支持