Python中Mock的示例

小好人lucky 7年前發布 | 18K 次閱讀 Python Python開發

一些常用的mock示例

先簡單定義個類,方便舉例:

class Person:
    def__init__(self):
        self.__age = 10
        
    defget_fullname(self, first_name, last_name):
        return first_name + ' ' + last_name
        
    defget_age(self):
        return self.__age
        
    @staticmethod
    defget_class_name():
        return Person.__name__

這個類里有兩個成員方法,一個有參數,一個無參數。還有一個靜態方法

mock成員方法

1. 使用Mock類,返回固定值

class PersonTest(TestCase):
    deftest_should_get_age(self):
        p = Person()
        
        # 不mock時,get_age應該返回10
        self.assertEqual(p.get_age(), 10)
        
        # mock掉get_age方法,讓它返回20
        p.get_age = Mock(return_value=20)
        self.assertEqual(p.get_age(), 20)
    
    deftest_should_get_fullname(self):
        p = Person()
        
        # mock掉get_fullname,讓它返回'James Harden'
        p.get_fullname = Mock(return_value='James Harden')
        self.assertEqual(p.get_fullname(), 'James Harden')

2. 校驗參數個數,再返回固定值

上面的例子你也許已經注意到了,調用p.get_fullname時沒有給任何的參數,但是依然可以工作。

如果想校驗參數需要用 create_autospec 模塊方法替代Mock類。

class PersonTest(TestCase):
    deftest_should_get_fullname(self):
        p = Person()
        
        p.get_fullname = create_autospec(p.get_fullname, return_value='James Harden')
        
        # 隨便給兩個參數,依然會返回mock的值
        self.assertEqual(p.get_fullname('1', '2'), 'James Harden') 
        
        # 如果參數個數不對,會報錯TypeError: missing a required argument: 'last_name'
        p.get_fullname('1')

3. 使用side_effect, 依次返回指定值

class PersonTest(TestCase):
    deftest_should_get_age(self):
        p = Person()
        
        p.get_age = Mock(side_effect=[10, 11, 12])
 
        self.assertEqual(p.get_age(), 10)
        self.assertEqual(p.get_age(), 11)
        self.assertEqual(p.get_age(), 12)

4. 根據參數不同,返回不同的值

class PersonTest(TestCase):
    deftest_should_get_fullname(self):
        p = Person()
        
        values = {('James', 'Harden'): 'James Harden', ('Tracy', 'Grady'): 'Tracy Grady'}
        p.get_fullname = Mock(side_effect=lambda x, y: values[(x, y)])
        
        self.assertEqual(p.get_fullname('James', 'Harden'), 'James Harden')
        self.assertEqual(p.get_fullname('Tracy', 'Grady'), 'Tracy Grady')

5. 拋出異常

class PersonTest(TestCase):
    deftest_should_raise_exception(self):
        p = Person()
        
        p.get_age = Mock(side_effect=TypeError('integer type'))
        # 只要調就會拋出異常
        self.assertRaises(TypeError, p.get_age)

6. 檢驗是否調用

class PersonTest(TestCase):
    deftest_should_validate_method_calling(self):
        p.get_fullname = Mock(return_value='James Harden')
 
        # 沒調用過
        p.get_fullname.assert_not_called()  # Python 3.5
 
        p.get_fullname('1', '2')
 
        # 調用過任意次數
        p.get_fullname.assert_called() # Python 3.6
        # 只調用過一次, 不管參數
        p.get_fullname.assert_called_once() # Python 3.6
        # 只調用過一次,并且符合指定的參數
        p.get_fullname.assert_called_once_with('1', '2')
 
        p.get_fullname('3', '4')
        # 只要調用過即可,必須指定參數
        p.get_fullname.assert_any_call('1', '2')
 
        # 重置mock,重置之后相當于沒有調用過
        p.get_fullname.reset_mock()
        p.get_fullname.assert_not_called()
        
        # Mock對象里除了return_value, side_effect屬性外,
        # called表示是否調用過,call_count可以返回調用的次數
        self.assertEqual(p.get_fullname.called, False)
        self.assertEqual(p.get_fullname.call_count, 0)
        
        p.get_fullname('1', '2')
        p.get_fullname('3', '4')
        self.assertEqual(p.get_fullname.called, True)
        self.assertEqual(p.get_fullname.call_count, 2)

mock靜態方法

靜態方法和模塊方法需要使用 patch 來mock。

1. 在測試方法參數中得到Mock對象

class PersonTest(TestCase):
    # 以字符串的形式列出靜態方法的路徑,在測試的參數里會自動得到一個Mock對象
    @patch('your.package.module.Person.get_class_name')
    deftest_should_get_class_name(self, mock_get_class_name):
        mock_get_class_name.return_value = 'Guy'
 
        self.assertEqual(Person.get_class_name(), 'Guy')

2. 在patch中設置Mock對象

class PersonTest(TestCase):
    mock_get_class_name = Mock(return_value='Guy')
 
    # 在patch中給出定義好的Mock的對象,好處是定義好的對象可以復用
    @patch('your.package.module.Person.get_class_name', mock_get_class_name)
    deftest_should_get_class_name(self):
        self.assertEqual(Person.get_class_name(), 'Guy')

3. 使用patch.object

class PersonTest(TestCase):
    mock_get_class_name = Mock(return_value='Guy')
 
    # 使用patch.object來mock,好處是Person類不是以字符串形式給出的
    @patch.object(Person, 'get_class_name', mock_get_class_name)
    deftest_should_get_class_name(self, ):
        self.assertEqual(Person.get_class_name(), 'Guy')

4. 使用with控制作用域

class PersonTest(TestCase):
    # 作用域之外,依然返回真實值
    deftest_should_get_class_name(self, ):
        mock_get_class_name = Mock(return_value='Guy')
        withpatch('your.package.module.Person.get_class_name', mock_get_class_name):
            self.assertEqual(Person.get_class_name(), 'Guy')
 
        self.assertEqual(Person.get_class_name(), 'Person')

mock鏈式調用

在django里,我們經常需要mock數據庫,而訪問數據庫時經常是鏈式調用,看個例子。

defget_person(name):
    return Person.objects.filter(name=name).order_by('age')

有個模塊方法,返回數據庫中所有指定name的人員,并按age排序

mock掉整個數據庫訪問

@patch('your.package.module.Person.objects.filter')
deftest_should_get_person(self, mock_filter):
    # 先得到一個filter的Mock對象,再在return_value中設置一個Mock對象,此時不需要自己再創建
    mock_filter.return_value.order_by.return_value = None
    
    self.assertIsNone(get_person())

 

來自:http://python.jobbole.com/87656/

 

 本文由用戶 小好人lucky 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!