How can I disable a third party API when executing Django unit tests?
I'm trying to build some unit tests against some code I have in Django that runs operations against a third party API. Specifically, I'm synchronizing some user data with MailChimp and using a library that implements the MailChimp API.
I have a custom class MailChimpAPI
that essentially acts as a thin wrapper around the Python library I'm using. Here are some relevant portions of the code:
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
...
I snipped out (...) most of the code that implements the business logic etc., but the salient aspect here is that I set the api
attribute to an instance of PyChimp (the third party library) in __init__()
, and all of the actual calls to that library are made in the _call()
function.
I'm somewhat new to unit testing, so I'm having a 开发者_运维百科hard time figuring out the best way to approach this. One idea I had was to instantiate the PyChimp library outside of my class and pass it into the constructor. Another idea is in testing this I could mock the _call
method so that it doesn't hit the actual library.
The number one issue I have is that, obviously, I don't want to execute any testing code against the actual API. So I'm trying to figure out the best way to "stub" or "mock" that API so that calls made to it during testing don't actually execute. Ideally, I'd like to have that mock API present as all tests in the Django test suite run. For example, I'm exploring the possibility of syncing a user to MailChimp whenever a User account is created via the post_save
signal. For that reason, I obviously don't want the tests that run in the Django Authentication app to trigger the actual API either.
I was hoping Django had some global setup / teardown hooks or signals I could work with, but there doesn't seem to be anything.
Does anyone have any suggestions for how to cleanly substitute a "live" API with a pretend one during testing? Or am I doing it totally wrong?
I am quite confident I can hack my way to a solution, but it would be great if someone would be kind of enough to share some wisdom about the "best" way to approach this kind of thing.
The best way to go about it is probably as you suggest, creating the api object outside and passing it to the constructor. This will allow you to replace the api class easily for testing.
You could create a MockPyChimp class, which has the same methods as the actual PyChimp api. This way you could easily reuse the same mock class across your tests.
I'm not familiar enough with python/django unit testing to be able to suggest any specific libraries to assist in this, but I would assume there exists some sort of mocking libraries or such.
Well the best way to "mock" a function call in Python is with the mock library. Also if you are using nose as your unit-test framework then a plugin worth considering is nose-blockage which ensures no API calls get through if your tests don't properly mock out everything.
精彩评论