我试图了解Python Mock以更好地对我的代码进行单元测试。我过去没有进行过多次单元测试,但我想强调它向前发展。使用mock.patch('某些')作为模拟""对于我的代码正在使用的模拟对象,语法似乎非常方便。这对于模仿数据库或API调用特别方便。
但是,我注意到我写的测试数量增加了,我测试中的重复次数也增加了。如果我在我的类(MyClass下面)中使用了需要模拟的多个类,我需要将它们模拟为多个测试,即使它们不是直接用于特定测试。例如:
with context("my test"):
with it('responds true'):
with mock.patch('lib.mymodule.ClassA') as MockClassA:
with mock.patch('lib.mymodule.ClassB') as MockClassB:
with mock.patch('lib.mymodule.ClassC') as MockClassC:
MockClassA.return_value = "bogus result"
f = MyClass("host", "user", "password")
self.assertEqual(f, "bogus result")
在这种情况下,MockClassA,B和C可能会与数据库通信或进行API调用,在测试过程中我实际上并不想这样做。但是由于我的课程正在使用每个,我需要为所有测试模拟所有这些。有更好的方法吗?
编辑:修复我的代码以反映我使用Mamba进行单元测试。我为最初没有提到这件事而道歉。
答案 0 :(得分:1)
patch()充当函数装饰器,类装饰器......
使用patch作为装饰器是提高可读性和简单性的最佳方法之一。你的案子成了
def TestHostRecordCreation(self):
@mock.patch('lib.mymodule.ClassC')
@mock.patch('lib.mymodule.ClassB')
@mock.patch('lib.mymodule.ClassA')
def test_create_record(self, MockClassA, MockClassB, MockClassC):
f = MyClass("host", "user", "password")
self.assertEqual(f, "bogus result")
此外,如果要为所有测试用例制作相同的补丁,可以装饰类而不是单个方法。 As documented here由一个patch
装饰器装饰一个类,就像修补所有以patch.TEST_PREFIX
开头的方法一样。在您的情况下,我们使用patch.TEST_PREFIX
的默认值,我们可以写:
@mock.patch('lib.mymodule.ClassC')
@mock.patch('lib.mymodule.ClassB')
@mock.patch('lib.mymodule.ClassA')
def TestHostRecordCreation(self):
def test_A(self, MockClassA, MockClassB, MockClassC):
f = MyClass("host", "user", "password")
self.assertEqual(f, "bogus result")
def test_B(self, MockClassA, MockClassB, MockClassC):
f = MyClass("myhost", "myuser", "password")
self.assertEqual(f, "other bogus result")
最后,您可以使用patch.multiple
修补一组属性。在那个特定的合成案例接缝非常强大,但在真正的单词案例中它的使用是非常罕见的:
@mock.patch.multiple('lib.mymodule', ClassA=mock.DEFAULT, ClassB=mock.DEFAULT, ClassC=mock.DEFAULT)
def TestHostRecordCreation(self):
def test_A(self, MockClassA, MockClassB, MockClassC):
f = MyClass("host", "user", "password")
self.assertEqual(f, "bogus result")
def test_B(self, MockClassA, MockClassB, MockClassC):
f = MyClass("myhost", "myuser", "password")
self.assertEqual(f, "other bogus result")
如果您需要创建对许多测试有用的对象(每个测试单元框架都有这样的东西),请考虑使用setUp()
和tearDown()
。您可以使用setUp()
和tearDown()
来启动和停止补丁上下文,但我的兴趣是装饰器和with
上下文更具可读性。