2014-09-05 36 views
2

我有一個被測系統(下面的類Printer),它使用另一個類(下面的類ContentContainer)。在一種方法(下面的方法retrieve_and_show_content)中,這個類被實例化。在這個方法的測試中(下面的方法test_printer_03),我想實例化一個模擬而不是真實的類。但是,它不能這樣工作。嘲諷在被測方法中實例化的Python類

我讀here,我應該改變名稱與另一個指向的對象。看起來我想要替換的對象的名稱只是ContentContainer,而我實際替換的對象的名稱是TestMockClass.ContentContainer。這個觀察是否正確?如果是這樣,我該如何改變這一點?如果我只是在補丁語句中刪除前綴TestMockClass,我會得到一個TypeError: Need a valid target to patch. You supplied: 'ContentContainer'

#TestMockClass.py 
import unittest 
from mock import Mock, patch 

class Printer(): 
    def __init__(self, name, cc): 
     self.name = name 
     self.cc = cc 
    def show_content(self): 
     text = '{0} says: {1}'.format(self.name, self.cc.content()) 
     return text 
    def retrieve_and_show_content(self): 
     cc_tmp = ContentContainer() 
     text = '{0} says: {1}'.format(self.name, cc_tmp.content()) 
     return text 

class ContentContainer(): 
    def __init__(self): 
     self.method_counter() 
    def content(self): 
     return 'Content from ContentContainer' 
    def method_counter(self): 
     pass 

class Test(unittest.TestCase): 

    '''No mocking''' 
    def test_printer_01(self): 
     cc = ContentContainer() 
     sut = Printer('P01', cc) 
     result = sut.show_content() 
     expected_result = 'P01 says: Content from ContentContainer' 
     self.assertEqual(result, expected_result, 
         msg = '\nRetrieved:\n{0} \nExpected:\n{1}'.format(result, expected_result)) 
     result = sut.retrieve_and_show_content() 
     expected_result = 'P01 says: Content from ContentContainer' 
     self.assertEqual(result, expected_result, 
         msg = '\nRetrieved:\n{0} \nExpected:\n{1}'.format(result, expected_result)) 

    '''Create a mock object, which is the input of the method under test''' 
    def test_printer_02(self): 
     mock_cc = Mock() 
     mock_cc.content.return_value = 'Mocked content' 
     sut = Printer('P02', mock_cc) 
     result = sut.show_content() 
     expected_result = 'P02 says: Mocked content' 
     self.assertEqual(result, expected_result, 
         msg = '\nRetrieved:\n{0} \nExpected:\n{1}'.format(result, expected_result)) 
     self.assertFalse(mock_cc.method_counter.called, 'Method method_counter shall not be called') 

    '''Create a mock class, which is instantiated inside the method under test''' 
    @patch('TestMockClass.ContentContainer') 
    def test_printer_03(self, mock_cc): 
     mock_cc.content.return_value = 'Mocked content' 
     sut = Printer('P03', mock_cc) 
     result = sut.retrieve_and_show_content() 
     expected_result = 'P03 says: Mocked content' 
     self.assertEqual(result, expected_result, 
         msg = '\nRetrieved:\n{0} \nExpected:\n{1}'.format(result, expected_result)) 
     self.assertFalse(mock_cc.method_counter.called, 'Method method_counter shall not be called') 

if __name__ == "__main__": 
    unittest.main() 

當這個單元測試運行時,輸出爲:

AssertionError: 
Retrieved: 
P03 says: Content from ContentContainer 
Expected: 
P03 says: Mocked content 

回答

6

兩件事情:

  1. 因爲ContentContainer現在是在同一個文件作爲測試,你確實需要patch __main__.ContentContainer

    @patch('__main__.ContentContainer') 
    
  2. 因爲ContentContainer是一類,而你在該類的實例上調用content,所以實際上你希望在該實例上模擬content,而不是在類上。因此,您需要:mock_cc.return_value.content.return_value = 'Mocked content'(請注意其中的.return_value以確保您嘲笑實例,而不是課程)。這是因爲調用一個類創建一個實例。所以,這個實例就是這個類的調用的返回值。

試驗應如此模樣:

@patch('__main__.ContentContainer') 
def test_printer_03(self, mock_cc): 
    mock_cc.return_value.content.return_value = 'Mocked content' 
    ... 
+1

冷卻後,從文檔 - '''這意味着您可以訪問「模擬實例」通過看嘲笑class.'的返回值'' – wwii 2014-09-05 16:30:06