[英]Python mock patch with sub module
我無法替換在另一個模塊中調用函數的簡單方法。 根據我對模擬的了解,您必須引用被調用的方法(在上下文中,而不是原始方法)。 以下是我正在運行的程序的簡化版本,希望它是我需要學習的關於模擬的簡單知識。 補丁僅打算用於Class和Class方法,還是在這里做其他錯誤?
謝謝,史蒂夫
myapp.models.py
from myapp.backends import get_backend
class BasicClass(models.Model):
@staticmethod
def basic_method()
be = get_backend()
print be
myapp.backends._ init _.py
def get_backend():
return 'original value'
test.py
# Referencing the import in myapp.models.basic_class
# vs directly importing myapp.backends
# as indicated here:
# http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch
from myapp.models import get_backend
from myapp.models.basic_class import BasicClass
class ParsersTest(TestCase):
@patch('myapp.models.get_backend')
def test_simplified(self, moves_backend):
# Assertion fails
assert get_backend is moves_backend
# Assuming that assertion fails is why the original return value is always returned
moves_backend.return_value = 'new return value'
BasicClass.basic_method()
使用模擬補丁進行修補的目的是替換對模塊的引用,因為該引用將存儲在sys.modules
並用對您模擬的引用替換。 這意味着修補模塊中的代碼將收到對模擬對象的引用。
在測試中,您正在使用get_backend
。 在應用裝飾器之前,它是直接從myapp.models
導入到測試模塊頂部的。 未修補。 您的補丁已就位,但僅適用於myapp.models
中引用導入在那里的get_backend
符號的代碼。
我知道這很令人困惑。 對我來說,這是開始模擬的最難的部分。 如果您的測試如下所示:
class ParsersTest(TestCase):
@patch('myapp.models.get_backend')
def test_simplified(self, moves_backend):
from myapp.models.basic_class import BasicClass
# Assertion should pass
BasicClass.basic_method()
moves_backend.assert_called_with()
moves_backend.return_value = 'new return value'
# As should this one (if you change the method to return instead of print)
self.assertEqual(BasicClass.basic_method(), 'new return value')
我認為您的考試會通過。 這里的主要區別是您沒有直接針對get_backend進行測試。 您正在測試一種在應用補丁之后使用導入的get_backend的方法。
更新
我能想到的唯一一件事是,我不喜歡使用實際的代碼,只是我不喜歡將補丁用作裝飾器,因為您對何時應用/刪除補丁的控制較少,並且擔心獲取參考通過args進入模擬。
嘗試上下文管理器樣式:
with mock.patch('my app.models.get_backend') as moves_backend:
#...
其余測試邏輯嵌套在該分支下。
更新第二部分
我剛剛在您的原始代碼中注意到, BasicClass
位於myapp.models.basic_class.py
。
如果這是你的補丁應該被應用到的情況下'myapp.models.basic_class.get_backend'
因為你是想修補參考get_backend
正在導入的myapp.models.basic_class
子模塊。
我想你誤會了。 請記住:在Python中,模塊和類成員(或多或少)只是簡單的變量。 它們可能恰好包含一個方法,但它們仍然只是變量。 當您從另一個模塊導入某些內容時,它只是在導入模塊上創建了一個變量並將一些對象放入其中。
裝飾器@patch('myapp.models.get_backend')
只是用模擬對象替換了myapp.models
上的變量。 這不是您想要的。 在應用補丁時, myapp.models.basic_class
已經導入,因此它已經從myapp.models
導入了對真實方法的myapp.models
。 (換句話說,它的get_backend
變量已經保存了real方法。)您要替換在myapp.models.basic_class
實際使用的變量,如下所示:
@patch('myapp.models.basic_class.get_backend')
這將填補get_backend
的變量myapp.models.basic_class
與模擬。 因此,當您調用BasicClass.basic_method()
, basic_method
將在myapp.models.basic_class.get_backend
變量中查找並找到一個模擬。
所以試試這個:
from myapp.models import basic_class
class ParsersTest(TestCase):
@patch('myapp.models.basic_class.get_backend')
def test_simplified(self, moves_backend):
assert basic_class.get_backend is moves_backend
moves_backend.return_value = 'new return value'
basic_class.BasicClass.basic_method()
話雖如此,請小心測試。 您不想以所有可能的方式測試所有內容。 在編寫之前,請考慮一下該測試為您提供的價值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.