mock非静态成员方法
1. 使用Mock类,将目标方法赋值为Mock(return_value=XXXX),返回固定值; 默认情况下被mock的方法入参个数及赋值情况不会对返回值产生影响;
2. 校验参数个数,再返回固定值, 需要用create_autospec模块方法替代Mock类。(参数的取值不会对结果产生影响,但是个数会对结果产生影响; 如果参数个数不对,会报错TypeError: missing a required argument: 'last_name')
格式如下: p.get_fullname = create_autospec(p.get_fullname, return_value='James Harden')
3. 使用side_effect, 依次返回指定值,如:Mock(side_effect=[10, 11, 12]), side_effect为有序列表
4. 根据参数不同,返回不同的值,需要借助side_effect及lambda语法进行mock:
示例:values = {('James', 'Harden'): 'James Harden', ('Tracy', 'Grady'): 'Tracy Grady'}
p.get_fullname = Mock(side_effect=lambda x, y: values[(x, y)])
5. 抛出异常,通过Mock(side_effect=TypeError('integer type')),side_effect指定异常类型, 通过self.assertRaises对异常进行断言
class PersonTest(TestCase):
def test_should_raise_exception(self):
p = Person()
p.get_age = Mock(side_effect=TypeError('integer type'))
# 只要调就会抛出异常
self.assertRaises(TypeError, p.get_age)
6. 检验是否调用:assert_not_called 是否被调用过;assert_called 调用过任意次数;assert_called_once 调用过一次; assert_any_call(XX,XX) 满足指定参数的方法是否被调用过; reset_mock 重置mock
class PersonTest(TestCase):
def test_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对象:以字符串的形式列出静态方法的路径,在测试的参数里会自动得到一个Mock对象
@patch('your.package.module.Person.get_class_name')
def test_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对象: 在patch中给出定义好的Mock的对象,好处是定义好的对象可以复用
mock_get_class_name = Mock(return_value='Guy')
@patch('your.package.module.Person.get_class_name', mock_get_class_name)
def test_should_get_class_name(self):
self.assertEqual(Person.get_class_name(), 'Guy')
3. 使用patch.objec t:使用patch.object来mock,好处是静态类不是以字符串形式给出的
mock_get_class_name = Mock(return_value='Guy')
@patch.object(Person, 'get_class_name', mock_get_class_name)
def test_should_get_class_name(self, ):
self.assertEqual(Person.get_class_name(), 'Guy')
4. 使用with控制作用域:作用域之外,依然返回真实值
def test_should_get_class_name(self, ):
mock_get_class_name = Mock(return_value='Guy')
with patch('your.package.module.Person.get_class_name', mock_get_class_name):
self.assertEqual(Person.get_class_name(), 'Guy') # 返回Guy
self.assertEqual(Person.get_class_name(), 'Person') # 返回真实值
5. mock链式调用
在django里,我们经常需要mock数据库,而访问数据库时经常是链式调用,看个例子。
def get_person(name):
return Person.objects.filter(name=name).order_by('age') # 返回数据库中所有指定name的人员,并按age排序
mock掉整个数据库访问:先得到一个filter的Mock对象,再在return_value中设置一个Mock对象,此时不需要自己再创建
@patch('your.package.module.Person.objects.filter')
def test_should_get_person(self, mock_filter):
mock_filter.return_value.order_by.return_value = None
self.assertIsNone(get_person())
网友评论