如何在执行Django单元测试时禁用第三方API?
我正在尝试为我在Django中写的一些代码建立单元测试,这些代码会对接一个第三方API。具体来说,我在和MailChimp同步一些用户数据,并使用一个实现了MailChimp API的库。
我有一个自定义类 MailChimpAPI
,它基本上是我使用的Python库的一个简单封装。以下是一些相关的代码片段:
class MailChimpAPI(object):
"""
A wrapper for implementing business logic and exception handling around
the PyChimp API
"""
...
def __init__(self, api_key, primary_list_id, merge_keys, use_secure=True, timeout=None):
...
# setup api
self.api_key = api_key
self.api = PyChimp(self.api_key)
...
...
def _call(self, method_name, args=[], kwargs={}):
"""Calls the PyChimp API directly"""
result = getattr(self.api, method_name)(*args, **kwargs)
if self.api.errorCode:
self._raise_mailchimp_exception()
else:
return result
...
我省略了大部分实现业务逻辑的代码,但这里的关键点是我在 __init__()
方法中将 api
属性设置为PyChimp(这个第三方库)的一个实例,所有实际对这个库的调用都是在 _call()
函数中进行的。
我对单元测试还不是很熟悉,所以在寻找最佳方法时遇到了一些困难。我想到的一个主意是,在我的类外部实例化PyChimp库,然后把它传递给构造函数。另一个想法是在测试时可以模拟 _call
方法,这样就不会真正调用到实际的库。
我面临的最大问题是,显然我不想在测试代码中执行任何对实际API的调用。因此,我正在寻找一种“替代”或“模拟”这个API的最佳方法,以便在测试期间对它的调用不会实际执行。理想情况下,我希望在Django测试套件运行时,这个模拟的API能够一直存在。例如,我正在探索在通过 post_save
信号创建用户账户时,如何将用户同步到MailChimp。因此,我显然也不希望在Django认证应用中运行的测试触发实际的API。
我希望Django有一些全局的设置/清理钩子或信号可以使用,但似乎没有这样的东西。
有没有人能给我一些建议,如何在测试期间干净地用一个“假”的API替代一个“真实”的API?还是说我完全搞错了?
我很有信心可以找到解决方案,但如果有人能分享一些关于处理这种情况的“最佳”方法的经验,那就太好了。
2 个回答
在Python中,最好的方法来“模拟”一个函数调用就是使用mock这个库。还有,如果你使用nose作为你的单元测试框架,那么一个值得考虑的插件是nose-blockage。这个插件可以确保如果你的测试没有正确模拟所有内容,就不会有API调用通过。
最好的办法可能就是你说的那样,把api对象放在外面创建,然后传给构造函数。这样做可以让你在测试的时候更方便地替换api类。
你可以创建一个叫MockPyChimp的类,它的方法和真实的PyChimp api一模一样。这样,你就可以在测试中轻松地重复使用这个模拟类。
我对python/django的单元测试不太熟悉,所以不能推荐具体的库来帮助你,但我想应该有一些模拟库之类的东西可以用。