”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > Pytest 和 PostgreSQL:每次测试的新数据库

Pytest 和 PostgreSQL:每次测试的新数据库

发布于2024-08-21
浏览:853

Pytest and PostgreSQL: Fresh database for every test

在 Pytest(每个人最喜欢的 Python 测试框架)中,fixture 是一段可重用的代码,它在测试进入之前安排 something,并在测试退出后进行清理。例如,临时文件或文件夹、设置环境、启动 Web 服务器等。在这篇文章中,我们将了解如何创建 Pytest 夹具,该夹具创建一个测试数据库(空或已知状态),该数据库获取清理,允许每个测试在完全干净的数据库上运行

目标

我们将使用 Psycopg 3 创建一个 Pytest 夹具来准备和清理测试数据库。因为空数据库对测试几乎没有帮助,所以我们将选择应用 Yoyo 迁移(在撰写本文时网站已关闭,请转到 archive.org 快照)来填充它。

因此,在此博文中创建的名为 test_db 的 Pytest 夹具的要求是​​:

  • 删除测试数据库如果在测试之前存在
  • 在测试之前创建一个空数据库
  • 可选地
  • 在测试之前应用迁移或创建测试数据
  • 提供到测试数据库的连接到测试
  • 测试后删除测试数据库(即使失败)
通过列出测试方法参数来请求它的任何测试方法:


def test_create_admin_table(test_db): ...
def test_create_admin_table(test_db):
    ...
将收到连接到测试数据库的常规 Psycopg 连接实例。测试可以做任何它需要的事情,就像普通的 Psycopg 常见用法一样,例如:


def test_create_admin_table(test_db): # 打开游标执行数据库操作 cur = test_db.cursor() # 传递数据来填充查询占位符并让 Psycopg 执行 # 正确的转换(没有 SQL 注入!) 当前.执行( “插入测试(数字,数据)值(%s,%s)”, (100,“abc'def”)) # 查询数据库并获取数据作为Python对象。 cur.execute("从测试中选择*") cur.fetchone() # 将返回 (1, 100, "abc'def") # 你可以使用 `cur.fetchmany()`, `cur.fetchall()` 返回一个列表 # 多个记录,甚至在光标上迭代 以 cur 记录: 打印(记录)
def test_create_admin_table(test_db):
    ...

动机和替代方案 看起来有一些 Pytest 插件承诺为依赖数据库的测试提供 PostgreSQL 固定装置。它们可能很适合你。
我尝试过 pytest-postgresql 它承诺相同。在编写自己的装置之前我已经尝试过它,但我无法让它为我工作。也许是因为他们的文档让我很困惑。

另一个,pytest-dbt-postgres,我根本没有尝试过。



项目文件布局

在经典的Python项目中,源代码位于src/中,测试位于tests/中:


├── src │ └── 杜沃克 │ ├── __init__.py │ └── 销售 │ └── new_user.py ├── 测试 │ ├──conftest.py │ └── 销售 │ └── test_new_user.py ├── 需求.txt └── yoyo.ini
def test_create_admin_table(test_db):
    ...
如果你使用像梦幻般的Yoyo这样的迁移库,迁移脚本可能在migrations/:


├── 迁移 ├── 20240816_01_Yn3Ca-sales-user-user-add-last-run-table.py ├── ...
def test_create_admin_table(test_db):
    ...
配置

我们的测试数据库夹具需要很少的配置:

  • 连接 URL - (无数据库)
  • 测试数据库名称 - 将为每个测试重新创建
  • (可选)
  • 迁移文件夹 - 应用于每个测试的迁移脚本
Pytest 有一个天然的地方 conftest.py 用于跨多个文件共享固定装置。灯具配置也会在那里:


# 没有数据库名称! TEST_DB_URL = “postgresql://localhost” TEST_DB_NAME =“test_tuvok” TEST_DB_MIGRATIONS_DIR = str(Path(__file__, "../../migrations").resolve())
def test_create_admin_table(test_db):
    ...
您可以从环境变量或任何适合您情况的值中设置这些值。

创建 test_db 夹具

了解

PostgreSQL和Psycopg库,在conftest.py中编写fixture:

@pytest.fixture def test_db(): # autocommit=True 不启动事务,因为 CREATE/DROP DATABASE # 不能在事务块中执行。 使用 psycopg.connect(TEST_DB_URL, autocommit=True) 作为 conn: cur = conn.cursor() # 创建测试数据库,之前删除 cur.execute(f'如果存在“{TEST_DB_NAME}”则删除数据库(FORCE)') cur.execute(f'创建数据库“{TEST_DB_NAME}”') # 返回到刚刚创建的测试数据库的(新)连接 # 不幸的是,您无法直接更改现有 Psycopg 连接的数据库。一旦建立了与特定数据库的连接,它就与该数据库绑定在一起。 使用 psycopg.connect(TEST_DB_URL, dbname=TEST_DB_NAME) 作为 conn: 产量康涅狄格州 cur.execute(f'如果存在“{TEST_DB_NAME}”则删除数据库(FORCE)')
def test_create_admin_table(test_db):
    ...
创建迁移固定装置

在我们的例子中,我们使用

Yoyo 迁移。将应用迁移编写为另一个名为 yoyo 的固定装置:

@pytest.fixture def yoyo(): # Yoyo 期望 `driver://user:pass@host:port/database_name?param=value`。 # 在传递的 URL 中我们需要 网址 = ( urlparse(TEST_DB_URL) 。 # 1) 使用 `postgresql psycopg` 更改驱动程序(架构部分)以使用 # psycopg 3(不是 2,即 `postgresql psycopg2`) _replace(scheme="postgresql psycopg") 。 # 2) 将数据库更改为测试数据库(其中将应用迁移) _replace(路径=TEST_DB_NAME) .geturl() ) 后端 = get_backend(url) 迁移 = read_migrations(TEST_DB_MIGRATIONS_DIR) 如果 len(迁移) == 0: raise ValueError(f"在 '{TEST_DB_MIGRATIONS_DIR}' 中找不到 Yoyo 迁移'") 与 backend.lock(): backend.apply_migrations(backend.to_apply(迁移))
def test_create_admin_table(test_db):
    ...
如果你想

将迁移应用到每个测试数据库,需要 yoyo 夹具用于 test_db 夹具:

@pytest.fixture def test_db(yoyo): ...
def test_create_admin_table(test_db):
    ...

仅将迁移应用于某些测试,需要单独使用 yoyo:

def test_create_admin_table(test_db, yoyo): ...
def test_create_admin_table(test_db):
    ...
结论

构建自己的装置来为您的测试提供一个干净的数据库对我来说是一次有益的经历,让我能够更深入地研究 Pytest 和 Postgres。

我希望本文对您自己的数据库测试套件有所帮助。请随时在评论中留下您的问题并祝您编码愉快!

版本声明 本文转载于:https://dev.to/liborjelinek/pytest-and-postgresql-fresh-database-for-every-test-4eni?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何检查对象是否具有Python中的特定属性?
    如何检查对象是否具有Python中的特定属性?
    方法来确定对象属性存在寻求一种方法来验证对象中特定属性的存在。考虑以下示例,其中尝试访问不确定属性会引起错误: >>> a = someClass() >>> A.property Trackback(最近的最新电话): 文件“ ”,第1行, AttributeError: SomeClass...
    编程 发布于2025-06-09
  • Android如何向PHP服务器发送POST数据?
    Android如何向PHP服务器发送POST数据?
    在android apache httpclient(已弃用) httpclient httpclient = new defaulthttpclient(); httppost httppost = new httppost(“ http://www.yoursite.com/script.p...
    编程 发布于2025-06-09
  • 为什么尽管有效代码,为什么在PHP中捕获输入?
    为什么尽管有效代码,为什么在PHP中捕获输入?
    在php ;?>" method="post">The intention is to capture the input from the text box and display it when the submit button is clicked.但是,输出...
    编程 发布于2025-06-09
  • 如何使用替换指令在GO MOD中解析模块路径差异?
    如何使用替换指令在GO MOD中解析模块路径差异?
    在使用GO MOD时,在GO MOD 中克服模块路径差异时,可能会遇到冲突,其中3个Party Package将另一个PAXPANCE带有导入式套件之间的另一个软件包,并在导入式套件之间导入另一个软件包。如回声消息所证明的那样: go.etcd.io/bbolt [&&&&&&&&&&&&&&&&...
    编程 发布于2025-06-09
  • 为什么PYTZ最初显示出意外的时区偏移?
    为什么PYTZ最初显示出意外的时区偏移?
    与pytz 最初从pytz获得特定的偏移。例如,亚洲/hong_kong最初显示一个七个小时37分钟的偏移: 差异源利用本地化将时区分配给日期,使用了适当的时区名称和偏移量。但是,直接使用DateTime构造器分配时区不允许进行正确的调整。 example pytz.timezone(...
    编程 发布于2025-06-09
  • Java的Map.Entry和SimpleEntry如何简化键值对管理?
    Java的Map.Entry和SimpleEntry如何简化键值对管理?
    的综合集合:在Java中介绍Java的Map.entry和SimpleEntry和SimpleEntry和SimpleEntry和SimpleEntry和SimpleEntry和SimpleEntry和SimpleEntry和SimpleEntry apry and Map。 地图。它具有两个通用...
    编程 发布于2025-06-09
  • 如何使用FormData()处理多个文件上传?
    如何使用FormData()处理多个文件上传?
    )处理多个文件输入时,通常需要处理多个文件上传时,通常是必要的。 The fd.append("fileToUpload[]", files[x]); method can be used for this purpose, allowing you to send multi...
    编程 发布于2025-06-09
  • Go语言垃圾回收如何处理切片内存?
    Go语言垃圾回收如何处理切片内存?
    Garbage Collection in Go Slices: A Detailed AnalysisIn Go, a slice is a dynamic array that references an underlying array.使用切片时,了解垃圾收集行为至关重要,以避免潜在的内存泄...
    编程 发布于2025-06-09
  • 对象拟合:IE和Edge中的封面失败,如何修复?
    对象拟合:IE和Edge中的封面失败,如何修复?
    To resolve this issue, we employ a clever CSS solution that solves the problem:position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%)...
    编程 发布于2025-06-09
  • Java中假唤醒真的会发生吗?
    Java中假唤醒真的会发生吗?
    在Java中的浪费唤醒:真实性或神话?在Java同步中伪装唤醒的概念已经是讨论的主题。尽管存在这种行为的潜力,但问题仍然存在:它们实际上是在实践中发生的吗? Linux的唤醒机制根据Wikipedia关于伪造唤醒的文章,linux实现了pthread_cond_wait()功能的Linux实现,利用...
    编程 发布于2025-06-09
  • 如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求模拟浏览器行为,以及伪造的用户代理提供了一个用户 - 代理标头一个有效方法是提供有效的用户式header,以提供有效的用户 - 设置,该标题可以通过browser和Acterner Systems the equestersystermery和操作系统。通过模仿像Chro...
    编程 发布于2025-06-09
  • Spark DataFrame添加常量列的妙招
    Spark DataFrame添加常量列的妙招
    在Spark Dataframe ,将常数列添加到Spark DataFrame,该列具有适用于所有行的任意值的Spark DataFrame,可以通过多种方式实现。使用文字值(SPARK 1.3)在尝试提供直接值时,用于此问题时,旨在为此目的的column方法可能会导致错误。 df.withCo...
    编程 发布于2025-06-09
  • 如何正确使用与PDO参数的查询一样?
    如何正确使用与PDO参数的查询一样?
    在pdo 中使用类似QUERIES在PDO中的Queries时,您可能会遇到类似疑问中描述的问题:此查询也可能不会返回结果,即使$ var1和$ var2包含有效的搜索词。错误在于不正确包含%符号。通过将变量包含在$ params数组中的%符号中,您确保将%字符正确替换到查询中。没有此修改,PDO...
    编程 发布于2025-06-09
  • 为什么我的CSS背景图像出现?
    为什么我的CSS背景图像出现?
    故障排除:CSS背景图像未出现 ,您的背景图像尽管遵循教程说明,但您的背景图像仍未加载。图像和样式表位于相同的目录中,但背景仍然是空白的白色帆布。而不是不弃用的,您已经使用了CSS样式: bockent {背景:封闭图像文件名:背景图:url(nickcage.jpg); 如果您的html,css...
    编程 发布于2025-06-09
  • 如何在Java字符串中有效替换多个子字符串?
    如何在Java字符串中有效替换多个子字符串?
    在java 中有效地替换多个substring,需要在需要替换一个字符串中的多个substring的情况下,很容易求助于重复应用字符串的刺激力量。 However, this can be inefficient for large strings or when working with nu...
    编程 发布于2025-06-09

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3