On Tue, Jun 21, 2011 at 7:30 AM, Nan <ringe...@gmail.com> wrote: > > > > 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. > > I'd be a little confused as to how to factor that out. I mean, in the > 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. >
You're right, the view does need to return an HttpResponse; there's nothing you can do about that. My point about tight coupling was that your view has also been made responsible for instantiating the API wrapper that it thinks it needs, and there's no way to tell it not to. In other frameworks, I would immediately suggest using dependency injection -- the view depends on an API wrapper instance, so it shouldn't be creating that instance; that should be provided to it: def my_view(request, api): ... result = api.call(msg) because then you could call my_view(request, theAPI()) in most circumstances, but my_view(request, fake_api) in test code. Unfortunately, the view is almost always called directly by Django, and you don't have much control over its parameters. > > 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. > > Yes, it is very complex: it has to assess about 30 different potential > 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? > That's what I was suggesting; that way the view becomes simple enough that anyone looking at it can be assured of its correctness, without a host of unit tests. Those tests can be applied to the functions that actually construct the messages. But, see below -- I didn't realise that this effort was to aid in refactoring the code. > > 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. > > > 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. > > Oh! Thank you! I didn't understand before how a mock API might make > 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. > If this is all for a refactoring, then you're probably on the right track there -- instrument the existing object for testing, rather than restructuring the view first. Get the code into a state where you can trust it, and then you can start restructuring it to make it more easily testable in the future. Good luck! :) -- Regards, Ian Clelland <clell...@gmail.com> -- You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to django-users@googlegroups.com. To unsubscribe from this group, send email to django-users+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-users?hl=en.