2016-02-29 34 views
4

我使用python mocks來聲明一個特定的對象是用正確的參數創建的。這是我的代碼的外觀:斷言__init__被調用了正確的參數

class Installer: 
    def __init__(foo, bar, version): 
     # Init stuff 
     pass 
    def __enter__(self): 
     return self 

    def __exit__(self, type, value, tb): 
     # cleanup 
     pass 

    def install(self): 
     # Install stuff 
     pass 

class Deployer: 
    def deploy(self): 
     with Installer('foo', 'bar', 1) as installer: 
      installer.install() 

現在,我想斷言,installer與正確的參數創建。這是我到目前爲止的代碼:

class DeployerTest(unittest.TestCase): 
    @patch('Installer', autospec=True) 
    def testInstaller(self, mock_installer): 
     deployer = Deployer() 
     deployer.deploy() 

     # Can't do this :-(
     mock_installer.__init__.assert_called_once_with('foo', 'bar', 1) 

這是錯誤我得到:

File "test_deployment.py", line .., in testInstaller 
    mock_installer.__init__.assert_called_once_with('foo', 'bar', 1) 
AttributeError: 'function' object has no attribute 'assert_called_once_with' 

這裏是固定的代碼(稱之爲test.py)。謝謝,所有!

import unittest 
from mock import patch 

class Installer: 
    def __init__(self, foo, bar, version): 
     # Init stuff 
     pass 

    def __enter__(self): 
     return self 

    def __exit__(self, type, value, tb): 
     # cleanup 
     pass 

    def install(self): 
     # Install stuff 
     pass 

class Deployer: 
    def deploy(self): 
     with Installer('foo', 'bar', 1) as installer: 
      installer.install() 

class DeployerTest(unittest.TestCase): 
    @patch('tests.test.Installer', autospec=True) 
    def testInstaller(self, mock_installer): 
     deployer = Deployer() 
     deployer.deploy() 

     # Can't do this :-(
     # mock_installer.__init__.assert_called_once_with('foo', 'bar', 1) 

     # Try this instead 
     mock_installer.assert_called_once_with('foo', 'bar', 1) 
+0

'Installer'應該如何作爲上下文管理器工作? – jonrsharpe

+0

對不起,我急於做一個簡明的例子,我忽略了'Installer'的上下文管理器位。現在填寫 – Srikanth

+1

這不完全相同,但你看看例如http://stackoverflow.com/a/6112456/3001761?請注意,就模擬而言,初始化只是對「mock_installer」本身的調用。 – jonrsharpe

回答

3

所以,你得到的錯誤信息實際上是因爲你沒有正確地檢查你的模擬。你必須在這裏理解的是,在你的裝飾器中,你最終會說,對Installer的調用將重新轉向Mock對象。

因此,對於任何關於修補位置的Installer()調用,返回值將調用Mock()

所以,你居然要檢查的說法簡直是在mock_installer,而不是mock_installer.__init__.

mock_installer.assert_called_once_with('foo', 'bar', 1) 

下面是對代碼進行了修改:

class DeployerTest(unittest.TestCase): 

    @patch('Installer', autospec=True) 
    def testInstaller(self, mock_installer): 
     deployer = Deployer() 
     deployer.deploy() 

     mock_installer.assert_called_once_with('foo', 'bar', 1) 

一點點額外的提供一些更多解釋的信息,如果您現在正在測試是否在您的上下文管理器中調用了安裝,則必須在此意識到您實際上必須檢查您的內部__enter__,因此結構將爲l IKE在此:

爲了清楚起見,在您的測試方法創建一個mock_obj和:現在

mock_obj = mock_installer.return_value 

,你的上下文管理器中,你會需要看看裏面調用到__enter__()。如果你不知道這是爲什麼,請閱讀上下文管理器。

因此,考慮到這一點,你只需執行你的支票:

mock_obj.__enter__().install.assert_called_once_with() 
+0

感謝您的回答。還有關於如何聲明'install()'被調用的有用提示。 – Srikanth

+0

@Srikanth非常歡迎! – idjaw

+0

其實'安裝'檢查不起作用。 'mock_installer .__輸入__。install.assert_called_once_with()'和'mock_installer.install.assert_called_once_with()'都會導致'AssertionError:預期的'install'被調用一次。稱爲0次.'。 – Srikanth

2

的問題是,Installer(...)不直接調用Installer.__init__;相反,因爲Installertype一個實例,type.__call__定義,你Installer()等同於

type.__call__(Installer, ...) 

導致以Installer.__new__(Installer, ...)通話,其返回值x與原來的參數一起傳遞給Installer.__init__

所有這些都是說,由於您綁定到Installer的模擬對象不是type的實例,因此以上都不適用。 Installer(...)只是一個模擬對象的調用,所以你直接檢查:

mock_installer.assert_called_once_with('foo', 'bar', 1) 
+0

非常好的解釋chepner! – idjaw