Pytest的Postgresql fixture和fixture工厂。
项目描述
pytest-postgresql
这是什么?
这是一个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()
如果您想自动将模式填充到数据库配置中,有两种方法
客户端配置特定
进程配置特定
两者都接受相同的可能加载器集合
SQL文件路径
加载函数导入路径(字符串)
实际的加载函数
该函数将接收 host、port、user、dbname 和 password 关键字参数,并需要在内部执行数据库连接。但是,您将能够运行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的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | f996637367e6aecebba1349da52eea95340bdb434c90e4b79739e62c656056e2 |
|
MD5 | f04e0b064516775517861b1a63d97be0 |
|
BLAKE2b-256 | 8851d56a56fd852f0e3da5d001cb90815c793f904e387495e63e22ea90d2de1d |
pytest_postgresql-6.1.1-py3-none-any.whl的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | bd4c0970d25685ac3d34d42263fcbfbf134bf02d22519fce7e1ccf4122d8b99a |
|
MD5 | 677439a2dbfd48a956a9c4f415e2b95e |
|
BLAKE2b-256 | dbc8ca523b7dac9253cbfd60466aa0993b4fe5c558844585e7a0c0ad21d00bec |