1. 什么是单元测试
单元测试是指在开发过程中,针对程序中的最小功能模块进行测试的一种测试方法。这些最小模块通常是由函数或方法来实现的,因为它们是程序的主要构成部分。
单元测试在软件开发中非常重要,因为它可以帮助开发人员更快地发现和修复代码中的错误。这使得开发人员可以编写更可靠、更稳健的代码。
2. 如何给对象打补丁
2.1 为什么需要给对象打补丁
在进行单元测试时,有时候我们需要模拟一些对象或者其属性的值,以便更好地测试程序的各个部分。而在实际的代码中,这些对象可能并不存在,或者属性的值与我们需要的不同。因此,为了更好地进行测试,我们需要给对象打补丁。
2.2 用mock模块给对象打补丁
Python的mock模块提供了一种非常方便的方式来给对象打补丁。mock不仅可以模拟对象,还可以模拟方法和属性。
下面我们来看一个例子:
class MyClass:
def foo(self):
return 1
def bar():
c = MyClass()
return c.foo()
这段代码中,我们定义了一个类MyClass和一个函数bar。在函数bar中,我们实例化了类MyClass,并调用了它的foo方法。现在我们想测试函数bar,但是我们发现在测试时由于某些原因无法使用类MyClass。此时我们可以使用mock模块来给它打补丁:
import mock
def test_bar():
# 创建一个示例对象
c = mock.Mock()
# 给示例对象的foo方法打补丁,使它返回2
c.foo.return_value = 2
# 在测试bar函数时使用打过补丁的对象
assert bar(c) == 2
在这个例子中,我们使用mock模块创建了一个示例对象c,并给它的foo方法打了一个补丁,使它返回2。在测试函数bar时,我们使用mock模块提供的对象c来替换原来的MyClass实例。这样,我们就可以测试函数bar了,而不必担心MyClass的实例无法使用等问题。
2.3 patch装饰器
除了手动创建mock对象并给它打补丁之外,mock库还提供了一个非常方便的修饰器来实现这个过程:patch。使用patch修饰器可以直接将mock对象注入到测试用例中。
下面我们用一个例子来演示patch装饰器的用法:
class MyClass2:
def foo(self, a):
return a
def bar2():
c = MyClass2()
return c.foo(1)
@mock.patch('__main__.MyClass2')
def test_bar2(mock_myclass):
# 给mock对象的foo方法打补丁,使它返回2
mock_myclass().foo.return_value = 2
# 测试bar2函数
assert bar2() == 2
在这个例子中,我们首先定义了一个MyClass2类和一个bar2函数。bar2函数与上例中的bar函数相似,都是实例化类并调用它的方法。在测试bar2函数时,我们使用patch修饰器来创建mock对象,并将它注入到测试用例中。
patch修饰器的参数是字符串,它表示要被patch的对象或属性。在这个例子中,'__main__.MyClass2'表示我们要给当前模块中的MyClass2类打补丁。
需要注意的是,在使用patch修饰器进行测试时,我们不需要手动调用stop()方法停止模拟。patch修饰器会自动处理这个过程。
2.4 side_effect参数
除了直接设置返回值之外,我们还可以使用side_effect参数来控制方法的行为,比如在调用方法时抛出异常等。
下面我们用一个例子来演示side_effect参数的用法:
class MyClass3:
def foo(self, a):
return a
def bar3():
c = MyClass3()
return c.foo(1)
@mock.patch('__main__.MyClass3')
def test_bar3(mock_myclass):
# 设置mock对象的foo方法的side_effect参数,使它在第一次被调用时抛出ValueError,
# 在第二次调用时返回2。
mock_myclass().foo.side_effect = [ValueError, 2]
# 第一次调用foo方法,此时会抛出ValueError
try:
bar3()
except ValueError as e:
assert str(e) == ''
# 第二次调用foo方法,此时会返回2
assert bar3() == 2
在这个例子中,我们首先定义了一个MyClass3类和一个bar3函数。在测试bar3函数时,我们使用patch修饰器来创建mock对象,并使用它的foo方法的side_effect参数来控制方法的行为。
我们给side_effect参数传递了一个列表,表示在第一次调用foo方法时,它会抛出ValueError异常,在第二次调用时,它会返回2。
需要注意的是,使用side_effect参数时,你需要确保返回的异常与方法的行为相符。否则,测试可能会通过而实际程序可能会出错。
3. 总结
在进行Python单元测试时,为了更好地测试程序的代码,我们常常需要模拟一些对象或者属性。为了方便模拟,我们可以使用mock模块来创建mock对象,并使用patch修饰器将它注入到测试用例中。在模拟对象时,我们可以使用side_effect参数来控制方法的行为,从而更好地模拟实际情况。