原文:https://segmentfault.com/a/1190000008753754数据库
先简单定义个类,方便举例:django
class Person: def __init__(self): self.__age = 10 def get_fullname(self, first_name, last_name): return first_name + ' ' + last_name def get_age(self): return self.__age @staticmethod def get_class_name(): return Person.__name__
这个类里有两个成员方法,一个有参数,一个无参数。还有一个静态方法segmentfault
class PersonTest(TestCase): def test_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) def test_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')
上面的例子你也许已经注意到了,调用p.get_fullname时没有给任何的参数,可是依然能够工做。
若是想校验参数须要用create_autospec模块方法替代Mock类。ide
class PersonTest(TestCase): def test_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')
class PersonTest(TestCase): def test_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)
class PersonTest(TestCase): def test_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')
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)
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)
静态方法和模块方法须要使用patch来mock。测试
class PersonTest(TestCase): # 以字符串的形式列出静态方法的路径,在测试的参数里会自动获得一个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')
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) def test_should_get_class_name(self): self.assertEqual(Person.get_class_name(), 'Guy')
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) def test_should_get_class_name(self, ): self.assertEqual(Person.get_class_name(), 'Guy')
class PersonTest(TestCase): # 做用域以外,依然返回真实值 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') self.assertEqual(Person.get_class_name(), 'Person')
在django里,咱们常常须要mock数据库,而访问数据库时常常是链式调用,看个例子。ui
def get_person(name): return Person.objects.filter(name=name).order_by('age')
有个模块方法,返回数据库中全部指定name的人员,并按age排序spa
mock掉整个数据库访问 code