1、Python mock库简介
2、mock库的使用
mock库简介
PART 01
1.1
mock库简介
Python的mock库是一个用于创建和测试模拟对象的库。模拟对象可以模拟真实对象的行为,例如函数、类或方法。这对于单元测试和测试驱动开发(TDD)非常有用,因为它允许你隔离代码并测试其特定部分。
1.2
mock特点
Python的mock库在测试中起着至关重要的作用,其具有以下特点:
-
隔离性:Mock提供了一种隔离被测试代码与其依赖之间的方式,从而使得测试更加稳定和可靠。通过隔离依赖,Mock可以在不同环境中测试同一份被测代码,或者在同一环境中测试不同的被测代码。
-
灵活性:Mock提供了一种非常灵活的方式来创建虚拟对象,从而模拟所需要的行为。Mock利用Python中的动态特性,可以很容易地模拟任何对象和方法,并定制它们的行为来满足测试需求。
-
可扩展性:Mock提供了一种轻量级的测试工具,但又非常易于扩展。可以使用Mock来实现更高级的测试功能,并且可以根据需要自定义Mock对象的行为,从而让测试更加丰富和精准。
-
可读性:Mock设计简单易懂,使用起来非常直观。Mock对象可以很容易地集成到测试框架中,并且可以根据需要自定义Mock对象的输出,从而让测试结果更具可读性。
-
可设置和可检查:使用Mock可以在测试中设置模拟对象的行为和检查模拟对象的行为。
-
易于维护:使用Mock可以使得测试代码更加简洁和易于维护。
1.3
mock使用场景
Python中的mock主要用于模拟测试环境中的对象,确保测试可以顺利进行,即使真实对象不可用或不易获取。以下是一些常见的使用场景:
-
模拟方法调用:当需要确认一个方法是否被系统中的其他部分调用过,并且调用时使用了正确的参数时,可以使用mock来模拟该方法的调用。
-
前后端并行开发:在前后端分离的开发模式中,后端服务可能尚未完成,此时可以使用mock来模拟后端服务,使得前端可以独立进行开发和测试。
-
模拟无法访问的资源:对于依赖第三方服务或接口的情况,如果这些服务或接口暂时无法访问,可以使用mock来模拟这些服务或接口的行为,保证开发和测试工作的连续性。
-
隔离系统:在进行单元测试时,为了避免脏数据干扰测试结果,可以使用mock来隔离系统,确保测试环境的稳定性。
-
提高测试效率:通过模拟耗时的操作,如网络请求、数据库操作等,可以提高测试的效率,使得测试过程更加快速和可靠。
-
保护敏感数据:在测试过程中,可能需要模拟涉及敏感数据的实际操作,如支付系统的扣款操作,此时使用mock可以避免实际操作对敏感数据的影响。
mock库使用
PART 02
2.1
mock库安装
在Python2.x中mock是一个单独模块,需要单独安装。
pip install mock
在Python3.x中,mock已经被集成到了unittest单元测试框架中,所以可以直接使用。
from unittest.mock import Mock
2.2
创建mock对象
在Python的unittest.mock库中,创建Mock对象时可以传递一些参数来定制Mock的行为。以下是一些常用的参数:
-
spec: 用于指定要模拟的对象的规范。它可以是任何对象,类,方法或属性。Mock对象将模拟该规范对象的所有方法,属性等。
-
spec_set: 与spec类似,但它是一个可选项。如果同时设置了spec和spec_set,那么spec_set将覆盖spec。
-
call_args: 一个元组或列表,用于指定模拟对象被调用时传递的参数。
-
call_args_list: 一个列表,包含多个元组或列表,用于指定模拟对象被多次调用时传递的参数。
-
return_value: 用于指定模拟对象被调用时返回的值。
-
side_effect: 用于指定模拟对象被调用时的副作用。它可以是任何可调用的对象或异常。
-
name: 用于指定模拟对象的名称。
-
create: 一个布尔值,用于指定是否创建模拟对象。默认为True。
-
wraps: 一个可调用对象,用于包装模拟对象。这可以用于模拟装饰器或函数修饰器。
-
**kwargs: 其他关键字参数将作为属性添加到模拟对象中。
rom unittest.mock import Mock
mock_obj = Mock(
spec=MyClass, # 模拟MyClass类
call_args=(3, 2), # 模拟调用时的参数为(3, 2)
return_value=5, # 模拟调用时返回5
side_effect=ValueError("Mock error"), # 模拟调用时引发ValueError异常
name="mocked_object", # 模拟对象的名称为"mocked_object"
wraps=original_function, # 将模拟对象包装为original_function的调用
**kwargs # 将其他关键字参数作为属性添加到模拟对象中
)
2.3
mock常用属性和方法
-
return_value: 设置模拟对象的返回值。这允许你指定当调用模拟对象时应该返回什么值。
from unittest.mock import MagicMock
# 创建一个模拟对象
mock_obj = MagicMock()
# 设置模拟对象的返回值为42
mock_obj.return_value = 42
# 调用模拟对象的方法,并获取返回值
result = mock_obj()
print(result) # 输出42
-
side_effect: 设置模拟对象调用时的负面效果。这可以用来模拟方法调用时可能抛出的异常,或者根据调用顺序返回不同的值。
from unittest.mock import MagicMock
# 创建一个模拟对象
mock_obj = MagicMock()
# 设置模拟对象在被调用时抛出异常
mock_obj.side_effect = Exception("Error")
# 调用模拟对象的方法,并捕获异常
try:
result = mock_obj()
except Exception as e:
print(e) # 输出"Error"
-
call_count: 获取模拟对象被调用的次数。这在测试中非常有用,因为它可以帮助你验证一个方法是否被调用了预期的次数。
from unittest.mock import MagicMock
# 创建一个模拟对象
mock_obj = MagicMock()
# 调用模拟对象的方法两次
mock_obj()
mock_obj()
# 获取模拟对象被调用的次数
count = mock_obj.call_count
print(count) # 输出2
-
assert_called_once_with(...): 检查模拟对象是否恰好被调用了一次,并且传递了指定的参数。
from unittest.mock import MagicMock
# 创建一个模拟对象
mock_obj = MagicMock()
# 调用模拟对象的方法,并传递参数"hello"和"world"
mock_obj("hello", "world")
# 检查模拟对象是否恰好被调用了一次,并且传递了参数"hello"和"world"
mock_obj.assert_called_once_with("hello", "world")
-
assert_called_any_with(...): 检查模拟对象是否至少被调用了一次,并且可以与任何给定的参数一起调用。
from unittest.mock import MagicMock
# 创建一个模拟对象
mock_obj = MagicMock()
# 调用模拟对象的方法,并传递不同的参数
mock_obj("hello", "world")
mock_obj("foo", "bar")
# 检查模拟对象是否至少被调用了一次,并且可以与任何给定的参数一起调用
mock_obj.assert_called_any_with("hello", "world")
mock_obj.assert_called_any_with("foo", "bar")
-
reset_mock(): 重置模拟对象的状态,清除所有之前的调用记录。
from unittest.mock import MagicMock
# 创建一个模拟对象
mock_obj = MagicMock()
# 调用模拟对象的方法两次
mock_obj()
mock_obj()
# 重置模拟对象的状态,清除所有之前的调用记录
mock_obj.reset_mock()
# 再次调用模拟对象的方法,并获取调用次数
count = mock_obj.call_count
print(count) # 输出0
2.3
mock的使用案例
案例一:模拟函数调用。使用Mock库来模拟函数my_function的调用。通过设置模拟函数的return_value属性为5,我们告诉模拟函数在调用时返回该值。然后,我们调用模拟函数并传入参数(3, 2),它将返回5。
from unittest.mock import Mock
def my_function(arg1, arg2):
return arg1 + arg2
mock_function = Mock(spec=my_function)
mock_function.return_value = 5
print(mock_function(3, 2)) # 输出: 5
案例二:模拟类的方法调用。使用Mock库来模拟类MyClass的方法my_method的调用。通过设置模拟对象的my_method方法的返回值为5,我们告诉模拟方法在调用时返回该值。然后,我们调用模拟对象的my_method方法并传入参数(3, 2),它将返回5。注意,由于模拟对象继承了类的方法,我们可以像调用普通方法一样调用模拟对象的方法。
from unittest.mock import Mock
class MyClass:
def my_method(self, arg1, arg2):
return arg1 + arg2
mock_obj = Mock(spec=MyClass)
mock_obj.my_method.return_value = 5
print(mock_obj.my_method(3, 2)) # 输出: 5
案例三:模拟对象属性访问。使用Mock库来模拟对象属性访问。通过设置模拟对象的属性my_attr为"mocked value",我们告诉模拟对象在访问该属性时返回该值。然后,我们访问模拟对象的my_attr属性,它将返回"mocked value"。由于模拟对象继承了类的属性,因此我们可以像访问普通属性一样访问模拟对象的属性。
from unittest.mock import Mock
class MyClass:
def __init__(self):
self.my_attr = None
mock_obj = Mock(spec=MyClass)
mock_obj.my_attr = "mocked value"
print(mock_obj.my_attr) # 输出: mocked value
案例四:模拟请求接口数据返回。在下面的案例中,我们使用了unittest模块自带的mock来模拟接口的返回数据。通过使用mock.patch装饰器,我们可以替换被测试函数中的requests.get方法,并设置mock_get的返回值。然后,我们调用被测试函数,并断言返回值是否符合预期。最后,我们还可以使用assert_called_once_with方法来断言mock_get方法是否被调用。
import unittest
from unittest import mock
# 要测试的函数
def get_data_from_api(url):
# 假设这里是调用了一个真实的接口,并返回了数据
response = requests.get(url)
return response.json()
class TestAPI(unittest.TestCase):
def test_get_data_from_api(self):
# 使用mock.patch装饰器来替换被测试函数中的requests.get方法
with mock.patch('requests.get') as mock_get:
# 设置mock_get的返回值
mock_get.return_value.json.return_value = {'status': 'success', 'data': 'mocked data'}
# 调用被测试函数
result = get_data_from_api('http://example.com/api')
# 断言返回值是否符合预期
self.assertEqual(result, {'status': 'success', 'data': 'mocked data'})
# 断言mock_get方法是否被调用
mock_get.assert_called_once_with('http://example.com/api')
if __name__ == '__main__':
unittest.main()
END