Unit testing in Django
- by acjohnson55
I'm really struggling to write effective unit tests for a large Django project. I have reasonably good test coverage, but I've come to realize that the tests I've been writing are definitely integration/acceptance tests, not unit tests at all, and I have critical portions of my application that are not being tested effectively. I want to fix this ASAP.
Here's my problem. My schema is deeply relational, and heavily time-oriented, giving my model object high internal coupling and lots of state. Many of my model methods query based on time intervals, and I've got a lot of auto_now_add going on in timestamped fields. So take a method that looks like this for example:
def summary(self, startTime=None, endTime=None):
# ... logic to assign a proper start and end time
# if none was provided, probably using datetime.now()
objects = self.related_model_set.manager_method.filter(...)
return sum(object.key_method(startTime, endTime) for object in objects)
How does one approach testing something like this?
Here's where I am so far. It occurs to me that the unit testing objective should be given some mocked behavior by key_method on its arguments, is summary correctly filtering/aggregating to produce a correct result?
Mocking datetime.now() is straightforward enough, but how can I mock out the rest of the behavior?
I could use fixtures, but I've heard pros and cons of using fixtures for building my data (poor maintainability being a con that hits home for me).
I could also setup my data through the ORM, but that can be limiting, because then I have to create related objects as well. And the ORM doesn't let you mess with auto_now_add fields manually.
Mocking the ORM is another option, but not only is it tricky to mock deeply nested ORM methods, but the logic in the ORM code gets mocked out of the test, and mocking seems to make the test really dependent on the internals and dependencies of the function-under-test.
The toughest nuts to crack seem to be the functions like this, that sit on a few layers of models and lower-level functions and are very dependent on the time, even though these functions may not be super complicated. My overall problem is that no matter how I seem to slice it, my tests are looking way more complex than the functions they are testing.