簡體   English   中英

如何在不知道python中模塊名稱的情況下模擬由測試模塊直接導入的函數

[英]How to mock a function imported directly by the tested module without knowing the module name in python

假設我在模塊中定義了一個函數:

module_a.py

def foo():
    return 10

我想創建一個API來對該函數進行修補:

patcher.py

import mock

class Patcher(object):

    def __enter__(self):
        self.patcher = mock.patch('module_a.foo',
                                  mock.Mock(return_value=15))

        self.patcher.start()

    def __exit__(self, *args):
        self.patcher.stop()

問題是,我不知道將使用我的API的模塊的名稱是什么。 因此,測試如下所示:

test1.py

from patcher import Patcher
import module_a

with Patcher():
    assert module_a.foo() == 15

將工作。 但是這樣寫的測試:

test2.py

from patcher import Patcher
from module_a import foo

with Patcher():
    assert foo() == 15

將失敗。

無論如何,沒有像第一個選項那樣讓API用戶編寫它的測試和模塊(!)嗎?

有一種在不知道修補程序發生位置的情況下“修補”功能的方法。 這是我的問題的要求,因為patcher是我的庫API,並且我不想使用我的庫獲得通往每個測試模塊的路徑。

我發現的解決方案是傳遞所有已加載的模塊,並嘗試在其中找到foo ,然后進行更改-由我自己實現補丁。 如果僅在啟動Patcher之后才進行導入,則我自己加載了模塊,並對其進行了更改。

現在,代碼將如下所示:

補丁

import sys
import mock

from module_a import foo as _orig_foo

import module_a

class Patcher(object):

    def __init__(self):
        self.undo_set = set()
        self.fake_foo = mock.Mock(return_value=15)

    def __enter__(self):
        modules = [
            module for mod_name, module in sys.modules.items() if
            mod_name is not None and module is not None and
            hasattr(module, '__name__') and
            module.__name__ not in ('module_a', 'patcher')
        ]

        for module in modules:
          for attr in dir(module):
                try:
                    attribute_value = getattr(module, attr)
                except (ValueError, AttributeError, ImportError):
                    # For some libraries, this happen.
                    continue

                if id(attribute_value) == id(_orig_foo):
                    setattr(module, attr, self.fake_foo)
                    self.undo_set.add((module, attr, attribute_value))

        # Solve for future imports
        module_a.foo = self.fake_foo


    def __exit__(self, *args):
        module_a.foo = _orig_foo
        for mod, attr, val in self.undo_set:
            setattr(mod, attr, val)
        self.undo_set = set()

暫無
暫無

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

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