如何在执行Django单元测试时禁用第三方API?

4 投票
2 回答
1323 浏览
提问于 2025-04-16 04:14

我正在尝试为我在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 个回答

0

在Python中,最好的方法来“模拟”一个函数调用就是使用mock这个库。还有,如果你使用nose作为你的单元测试框架,那么一个值得考虑的插件是nose-blockage。这个插件可以确保如果你的测试没有正确模拟所有内容,就不会有API调用通过。

1

最好的办法可能就是你说的那样,把api对象放在外面创建,然后传给构造函数。这样做可以让你在测试的时候更方便地替换api类。

你可以创建一个叫MockPyChimp的类,它的方法和真实的PyChimp api一模一样。这样,你就可以在测试中轻松地重复使用这个模拟类。

我对python/django的单元测试不太熟悉,所以不能推荐具体的库来帮助你,但我想应该有一些模拟库之类的东西可以用。

撰写回答