簡體   English   中英

Pytest monkeypatch 不適用於導入的函數

[英]Pytest monkeypatch isn't working on imported function

假設一個項目中有兩個包: some_packageanother_package

# some_package/foo.py:
def bar():
    print('hello')
# another_package/function.py
from some_package.foo import bar

def call_bar():
    # ... code ...
    bar()
    # ... code ...

我想測試another_package.function.call_bar some_package.foo.bar因為它有一些網絡 I/OI 想要避免。

這是一個測試:

# tests/test_bar.py
from another_package.function import call_bar

def test_bar(monkeypatch):
    monkeypatch.setattr('some_package.foo.bar', lambda: print('patched'))
    call_bar()
    assert True

令我驚訝的是,它輸出hello而不是patched 我試圖調試這個東西,在測試中放置了一個IPDB斷點。 當我手動導入some_package.foo.bar斷點和呼叫后bar()我得到patched

在我的真實項目中,情況更有趣。 如果我在項目根目錄中調用 pytest 我的函數沒有被修補,但是當我指定tests/test_bar.py作為參數時 - 它可以工作。

據我了解,它與from some_package.foo import bar語句有關。 如果在發生monkeypatching 之前執行它,則修補失敗。 但是在上面示例中的精簡測試設置中,修補在兩種情況下都不起作用。

為什么它在遇到斷點后可以在 IPDB REPL 中工作?

雖然Ronny 的回答有效,但它迫使您更改應用程序代碼。 一般來說,您不應該為了測試而這樣做。

相反,您可以明確修補第二個包中的對象。 在 unittest 模塊文檔中提到。

monkeypatch.setattr('another_package.bar', lambda: print('patched'))

命名導入為對象創建一個新名稱。 如果隨后替換對象的舊名稱,則新名稱不受影響。

導入模塊並使用module.bar代替。 那將始終使用當前對象。


編輯:

import module 

def func_under_test():
  module.foo()

def test_func():
   monkeypatch.setattr(...)
   func_under_test

正如亞歷克斯所說,你不應該為你的測試重寫你的代碼。 我遇到的問題是要修補的路徑。

鑒於代碼:

應用程序/處理程序/tasks.py

from auth.service import check_user

def handle_tasks_create(request):
  check_user(request.get('user_id'))
  create_task(request.body)
  return {'status': 'success'}

你對monkeypatch check_user第一直覺,像這樣:

monkeypatch.setattr('auth.service.check_user', lambda x: return None)

但是您要做的是修補tasks.py的實例。 可能這就是你想要的:

monkeypatch.setattr('app.handlers.tasks.check_user', lambda x: return None)

雖然給出的答案已經很好,但我希望這能帶來更完整的背景。

OP問題的正確答案:

monkeypatch.setattr('another_package.function.bar', lambda: print('patched'))

您的函數可能未被修補的另一個可能原因是您的代碼是否使用了多處理。

在 macOS 上,新進程的默認啟動方法已從fork更改為spawn 如果使用spawn ,則會啟動一個全新的 Python 解釋器進程,忽略您最近修補的函數。

修復:將默認啟動方法設置為fork

import multiprocessing

multiprocessing.set_start_method('fork', force=True)

您可以將此代碼段添加到tests/文件夾中的conftest.py

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM