In most situations, my app, upon receiving an HTTP request, sends data
to a third-party API, and returns an empty HttpResponse. I need to
test that the correct data is sent to the third-party API based on
internal application state. I'm perplexed as to how to intercept this
data in a unit test.
Here's a sort of dummy version of the code in question. What I need
to be able to test is that depending on the data in the CurrentState
object, the value of the "message" parameter sent by the API call is
correct. Any suggestions?
Mock is one good solution. Here's what I've done in the past
(basically half-assed mock):
1. Have representative data sets that are good for the service (eg.
whatever you send to them, and whatever they send you in return).
2. Monkey patch the call:
def hackety_patch():
from StringIO import StringIO
data = StringIO(testdata_response_from_API)
data.seek(0)
return data.read()
# in TestCase subclass
def setUp(self):
third_party.api.urllib2.urlopen = hackety_patch
def tearDown(self):
third_party.api.urllib2.urlopen = urllib2.urlopen
3. Break up your API calling code into more testable units to truly
isolate your independent code from the API calling code. It'll be much
easier to catch problems in the API integration code.
Hm, I'm not worried about receiving a valid response from the third-
party API, just about testing the value of the "msg" parameter that's
passed into it. I need to test the msg parameter because it is in
turn essentially a proxy for which state was reached in my_view.
my_view is actually a great deal more complex than in the example, and
is indeed broken into many smaller function calls. I need to unit
test to make sure that the logic is correct -- and since all those
function calls return empty HttpResponse objects, I can't use their
return values to test the correctness of their logic.
Just brainstorming here, could there be a way around this by placing a
logging call of some sort in theAPI.call() that would only be executed
during unit testing, and then to test the contents of the log?
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
I'd be a little confused as to how to factor that out. I mean, in the
> Your view function may indeed be too complex to test properly, and it sounds
> like it is too tightly coupled with with the API call -- there is no way to
> call the view without having it call the actual 3rd-party API as imported at
> the top of the file.
actual app that call is refactored behind a function that wraps the
third-party API, and I could theoretically monkey-patch something over
that function call for unit testing. But the view still has to return
an HttpResponse, and a blank one.
Yes, it is very complex: it has to assess about 30 different potential
> The other way is to make your view function as simple as possible -- simple
> enough that it is obviously correct, without any unit tests. Take
> *everything* that can affect the message which is passed to the API, and
> abstract that into another function that the view can call. Then test *that*
> function as a unit.
states via nested conditionals. It's actually broken down into a
number of sub-functions that each deal with a few sub-states; and
while refactoring might be a good idea, I wouldn't want to even
attempt that before having some good unit tests in place!
I think what you're suggesting (correct me if I'm wrong) is to have
the top-level view function be the only place the API is called, and
to use only a single call; every other sub-function that reaches an
exit state should instead return a string containing the message to
send?
Thinking aloud, my concern then becomes that some of the sub-functions
must actually make multiple API calls. So we'd have to be returning
tuples or something instead.
Oh! Thank you! I didn't understand before how a mock API might make
> If theAPI is a class, then give it methods like setTestMode() and
> resetTestMode(), and use them in your setUp and tearDown methods. Then, if
> test mode is enabled, don't actually make the third party call, just store
> the passed-in message in a module-level or class-level list and return
> success. After your view returns, check the list to see that the message was
> correct.
that possible -- it makes a lot more sense now. That soudns like the
perfect way to do it.
At least until we've got enough tests to make sure the refactoring is
correct, I need to be able to create the tests without touching the
actual view logic, so testing what the API receives makes the most
sense to me.
> I'm not testing the third-party service. I need to test *what I send
> to them*.
Use Mock and assert_called_with:
http://www.voidspace.org.uk/python/mock/mock.html#mock.Mock.assert_called_with
In this case you'd set theAPI.call as your mock and check that under different conditions it is called correctly.
--
Andy McKay
an...@clearwind.ca
twitter: @andymckay