Python单元测试中的Mock
在单元测试中,通过Mock可以将系统的其它部分隔离,方便对目标进行测试。
在Django项目中,经常需要patch model的方法避免访问数据库,方便测试,下面是一些案例。
Patch Django Manager 函数#
例如有一个类 Agent 可以通过 Agent.objects.get_query_by_uuid 来获取一个实例,为了避免调用实际的函数读取数据库,可以用下面的方法:
def test_something(mocker):
agent = Agent(uuid='xxxxxxxx')
agent_get_mock = mocker.patch('package.Agent.objects.get_query_by_uuid')
agent_get_mock.return_value = agent
这里使用了 pytest-mock 来访问mock功能。
这个方法也适用于Django Manager的默认函数,例如:get() 函数。
Patch Django objects.filter 函数#
例如代码中有类似这样的访问:
o = SomeThing.objects.filter(a='b').first()
可以用下面的代码进行Patch,避免访问数据库并返回自己创建的对象:
something_mock = mocker.patch('something_package_path.SomeThing.objects.filter')
something_mock.return_value.first.return_value = SomeThing(a='b')
有时需要在不同参数时返回不同的对象,可以用 side_effect 将调用 patch 到一个函数中进行处理:
def mock_some_thing_filter(*_args, **kwargs):
result_mock = Mock()
if kwargs['a'] == 'b':
result_mock.first.return_value = SomeThing(a='b')
if kwargs['a'] == 'c':
result_mock.first.return_value = SomeThing(a='c')
return result_mock
def test_something(mocker):
mocker.patch('something_package_path.SomeThing.objects.filter').side_effect = mock_some_thing_filter
修改类的成员函数的返回值#
有时在代码的深层会访问某个对象实例的成员函数,例如:访问 Agent.get_concurrency() 函数,若想要测试时所有 Agent 对象的 get_concurrency() 函数都返回 1,可以用下面的方法patch:
mocker.patch.object(Agent, 'get_concurrency').return_value = 1