簡體   English   中英

帶有子模塊的Python模擬補丁

[英]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.

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