」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > Pytest 和 PostgreSQL:每次測試的新資料庫

Pytest 和 PostgreSQL:每次測試的新資料庫

發佈於2024-08-21
瀏覽:377

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]刪除
最新教學 更多>
  • 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
  • 在UTF8 MySQL表中正確將Latin1字符轉換為UTF8的方法
    在UTF8 MySQL表中正確將Latin1字符轉換為UTF8的方法
    在UTF8表中將latin1字符轉換為utf8 ,您遇到了一個問題,其中含義的字符(例如,“jáuòiñe”)在utf8 table tabled tablesset中被extect(例如,“致電。為了解決此問題,您正在嘗試使用“ mb_convert_encoding”和“ iconv”轉換受...
    程式設計 發佈於2025-06-09
  • 如何使用Depimal.parse()中的指數表示法中的數字?
    如何使用Depimal.parse()中的指數表示法中的數字?
    在嘗試使用Decimal.parse(“ 1.2345e-02”中的指數符號表示法表示的字符串時,您可能會遇到錯誤。這是因為默認解析方法無法識別指數符號。 成功解析這樣的字符串,您需要明確指定它代表浮點數。您可以使用numbersTyles.Float樣式進行此操作,如下所示:[&& && && ...
    程式設計 發佈於2025-06-09
  • 如何正確使用與PDO參數的查詢一樣?
    如何正確使用與PDO參數的查詢一樣?
    在pdo 中使用類似QUERIES在PDO中的Queries時,您可能會遇到類似疑問中描述的問題:此查詢也可能不會返回結果,即使$ var1和$ var2包含有效的搜索詞。錯誤在於不正確包含%符號。 通過將變量包含在$ params數組中的%符號中,您確保將%字符正確替換到查詢中。沒有此修改,PD...
    程式設計 發佈於2025-06-09
  • FastAPI自定義404頁面創建指南
    FastAPI自定義404頁面創建指南
    response = await call_next(request) if response.status_code == 404: return RedirectResponse("https://fastapi.tiangolo.com") else: ...
    程式設計 發佈於2025-06-09
  • Go語言如何動態發現導出包類型?
    Go語言如何動態發現導出包類型?
    與反射軟件包中的有限類型的發現能力相反,本文探索了替代方法,探索了在Runruntime。 go import( “ FMT” “去/進口商” ) func main(){ pkg,err:= incorter.default()。導入(“ time”) 如果er...
    程式設計 發佈於2025-06-09
  • 在GO中構造SQL查詢時,如何安全地加入文本和值?
    在GO中構造SQL查詢時,如何安全地加入文本和值?
    在go中構造文本sql查詢時,在go sql queries 中,在使用conting and contement和contement consem per時,尤其是在使用integer per當per當per時,per per per當per. [&​​​​&&&&&&&&&&&&&&&默元組方...
    程式設計 發佈於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
  • 反射動態實現Go接口用於RPC方法探索
    反射動態實現Go接口用於RPC方法探索
    在GO 使用反射來實現定義RPC式方法的界面。例如,考慮一個接口,例如:鍵入myService接口{ 登錄(用戶名,密碼字符串)(sessionId int,錯誤錯誤) helloworld(sessionid int)(hi String,錯誤錯誤) } 替代方案而不是依靠反射...
    程式設計 發佈於2025-06-09
  • 如何檢查對像是否具有Python中的特定屬性?
    如何檢查對像是否具有Python中的特定屬性?
    方法來確定對象屬性存在尋求一種方法來驗證對像中特定屬性的存在。考慮以下示例,其中嘗試訪問不確定屬性會引起錯誤: >>> a = someClass() >>> A.property Trackback(最近的最新電話): 文件“ ”,第1行, AttributeError: SomeClass...
    程式設計 發佈於2025-06-09
  • PHP與C++函數重載處理的區別
    PHP與C++函數重載處理的區別
    作為經驗豐富的C開發人員脫離謎題,您可能會遇到功能超載的概念。這個概念雖然在C中普遍,但在PHP中構成了獨特的挑戰。讓我們深入研究PHP功能過載的複雜性,並探索其提供的可能性。 在PHP中理解php的方法在PHP中,函數超載的概念(如C等語言)不存在。函數簽名僅由其名稱定義,而與他們的參數列表無關...
    程式設計 發佈於2025-06-09
  • C++成員函數指針正確傳遞方法
    C++成員函數指針正確傳遞方法
    如何將成員函數置於c 的函數時,接受成員函數指針的函數時,必須同時提供對象的指針,並提供指針和指針到函數。需要具有一定簽名的功能指針。要通過成員函數,您需要同時提供對象指針(此)和成員函數指針。這可以通過修改Menubutton :: SetButton()(如下所示:[&& && && &&華)...
    程式設計 發佈於2025-06-09
  • 為什麼PHP的DateTime :: Modify('+1個月')會產生意外的結果?
    為什麼PHP的DateTime :: Modify('+1個月')會產生意外的結果?
    使用php dateTime修改月份:發現預期的行為在使用PHP的DateTime類時,添加或減去幾個月可能並不總是會產生預期的結果。正如文檔所警告的那樣,“當心”這些操作的“不像看起來那樣直觀。 ; $ date->修改('1個月'); //前進1個月 echo $ date->...
    程式設計 發佈於2025-06-09
  • 如何實時捕獲和流媒體以進行聊天機器人命令執行?
    如何實時捕獲和流媒體以進行聊天機器人命令執行?
    在開發能夠執行命令的chatbots的領域中,實時從命令執行實時捕獲Stdout,一個常見的需求是能夠檢索和顯示標準輸出(stdout)在cath cath cant cant cant cant cant cant cant cant interfaces in Chate cant inter...
    程式設計 發佈於2025-06-09
  • Python環境變量的訪問與管理方法
    Python環境變量的訪問與管理方法
    Accessing Environment Variables in PythonTo access environment variables in Python, utilize the os.environ object, which represents a mapping of envir...
    程式設計 發佈於2025-06-09

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3