一、Mock模块
为什么使用mock:
在我看来实际中用到mock的场景:
- 有个函数,我们不关心他的具体实现细节,只想要他的返回。这时就可以mock这个函数的返回
- mock对象来模拟一个需要使用的资源(?)
>>> import mock
>>> dir(mock.Mock())
['assert_any_call', 'assert_called', 'assert_called_once', 'assert_called_once_with', 'assert_called_with',
'assert_has_calls', 'assert_not_called', 'attach_mock', 'call_args', 'call_args_list', 'call_count',
'called', 'configure_mock', 'method_calls','mock_add_spec', 'mock_calls', 'reset_mock', 'return_value',
'side_effect']
一、Mock的构造器
init是mock对象的构造器,name是mock对象的唯一标识;spec设置的是mock对象的属性,可以是property或者方法,也可以是其他的列表字符串或者其他的python类;return_value设置的是,当这个mock对象被调用的时候,显示出的结果就是return_value的值;side_effect是和return_value是相反的,覆盖了return_value,也就是说当这个mock对象被调用的时候,返回的是side_effect的值,而不是return_value。
1、name
# name 是这个Mock实例的唯一标识 ,id是什么?内存,还是实例的编号?
>>> a = mock.Mock(name='name_1')
>>> print a
<Mock name='name_1' id='139821809679056'>
2、 return_value
(a)、return_value指定的是某个值
# 这个b可以是函数名(add),类的函数名(Person.add)
# b应该是类的实例,可是b()返回 return_value,这个是怎么实现的?
>>> b = mock.Mock(name='name_3', return_value=100)
>>> b
<Mock name='name_3' id='139821809217744'>
>>> print b()
100
>>> print type(b)
<class 'mock.mock.Mock'>
(b)、return_value指定的是类的实例
import unittest
import mock
class Person():
arg_0 =5
def __init__(self):
self.arg_1 = 10
pass
def add(self):
return 20
p = Person()
mock_obj = mock.Mock(return_value=p)
a = mock_obj()
# mock_obj = mock.Mock() 只是返回一个mock实例
# 要他的返回值时a = mock_obj (),a就是return_value
print 'a: ',a
>>>a: <__main__.Person instance at 0xd46a28>
print 'a.add: ',a.add
>>>a.add: <bound method Person.add of <__main__.Person instance at 0xd46a28>>
print 'a.add(): ',a.add()
>>>a.add(): 20
print 'a.arg_1: ',a.arg_1
>>>a.arg_1: 10
print a.arg_0
>>>5
3、side_effect参数:
(a)、指定的参数的值是异常
# 返回的异常覆盖了return_value
>>> import mock
>>> import unittest
>>> mock_object = mock.Mock(return_value=10, side_effect=StandardError)
>>> b = mock_object()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/site-packages/mock/mock.py", line 1062, in __call__
return _mock_self._mock_call(*args, **kwargs)
File "/usr/lib/python2.7/site-packages/mock/mock.py", line 1118, in _mock_call
raise effect
StandardError
(b)、指定的参数的值是一个list或者tuple:
这个就不展开写了,目前我还没用到
二、Mock实现的例子
- Mock一个函数。
根据上面理解的Mock,可以这么实现:
import unittest
import mock
def multiple(a, b):
return a*b
class TestProducer(unittest.TestCase):
def setUp(self):
pass
def test_multiple(self):
multiple = mock.Mock(return_value=3)
self.assertEqual(multiple(8, 14), 3)
if __name__ == "__main__":
unittest.main()
另一种方法就是mock.patch
# 为啥执行报错???
# TypeError: Need a valid target to patch. You supplied: 'multiple'
# mock.patch('multiple') 修改为mock.patch('__main__.multiple')
import unittest
import mock
class Calculator(object):
def add(self, a, b):
return a+b
def multiple(a, b):
return a*b
class TestProducer(unittest.TestCase):
def setUp(self):
self.calculator = Calculator()
# mock.patch('multiple')一定
# mock_multiple 是不是可以随便命名???
@mock.patch('multiple')
def test_multiple(self, mock_multiple):
mock_multiple.return_value = 3
self.assertEqual(multiple(8, 14), 3)
if __name__ == "__main__":
unittest.main()
- Mock一个对象里面的方法
# a31.py
def add4(a,b):
return a+b
class Add():
def __init__(self):
pass
def add5(self,a,b):
return a+b
import unittest
import mock
from a31 import Add, add4
class Calculator(object):
def add(self, a, b):
return a+b
def add2(self,a, b):
return a+b
class TestProducer(unittest.TestCase):
def setUp(self):
self.calculator = Calculator()
@mock.patch.object(Calculator, 'add')
def test_add(self, mock_add):
mock_add.return_value = 3
self.assertEqual(self.calculator.add(8, 14), 3)
# mock.patch 必须完整的路径
@mock.patch('__main__.Calculator.add2')
def test_add2(self,mock_add2):
mock_add2.return_value = 10
self.assertEqual(self.calculator.add2(10,20),10)
# from a31 import Add, add4
# @mock.patch('__main__.Calculator.add2') 也可以
@mock.patch('a31.Add.add5')
def test_add3(self,mock_add5):
ab = Add()
mock_add5.return_value = 10
self.assertEqual(ab.add5(10,20),10)
@mock.patch('__main__.add4')
def test_add3(self,mock_add4):
#ab = Add()
mock_add4.return_value = 10
self.assertEqual(add4(10,20),10)
if __name__ == "__main__":
unittest.main()
# 如果是
import a31
@mock.patch('a31.Add.add5')
def test_add3(self,mock_add5):
ab = a31.Add()
mock_add5.return_value = 10
self.assertEqual(ab.add5(10,20),10)
@mock.patch('a31.add4')
def test_add3(self,mock_add4):
#ab = Add()
mock_add4.return_value = 10
self.assertEqual(a31.add4(10,20),10)
总结:
- patch和patch.object,一定要指定mock对象的具体位置。
- patch,方法和类都可以mock。方法和类在同一个文件中时要加__main__.
如@mock.patch(’_ main_.add4’) - mock.patch.object(a31.Add, ‘add5’)应该是只可以mock类中的方法。第一个参数指定类的位置,第二个参数指定类中的方法。
# 函数参数mock_add4只是形参,实际入参是add4。所以 mock_add4可以随便写什么。
# 但最好还是和要mock的函数一致,便于认识。
@mock.patch('a31.add4')
def test_add3(self,mock_add4):
print mock_add4
# 输出..<MagicMock name='add4' id='38723792'>
- 让Mock的函数抛出exception
import os
import mock
import unittest
def is_error():
try:
os.mkdir("11")
return False
except Exception as e:
print 'Exception'
return True
class TestProducer(unittest.TestCase):
@mock.patch('os.mkdir')
def test_exception(self, mock_mkdir):
mock_mkdir.side_effect = Exception
self.assertEqual(is_error(), True)
if __name__ == '__main__':
unittest.main()