Reputation: 13198
I've recently learned about Test Driven Development and want to give it a shot while developing a new app in my Django project. I've been reading Test-Driven Development with Python which is great. However, I've sometimes found the example (To Do lists) in the book to be too simple- for instance, when testing Models is introduced, the author has a test that creates to objects, saves them, and then pulls the objects from the database to check their values. Sure, that's easy when your model only has one ModelField.
But what about when your model has twenty ModelFields? Should you have one test that creates an object, with all of its Fields, then saves that object, and then checks the value of each field? Is it better to make individual tests for each field?
In my specific case, I have a model with about five required fields, and then about fifteen more fields that are optional. My thought right now is to first have a function within my TestCase class that creates an object of this Model with the default fields. Then, I will have a test to make sure that object saves normally, and then another test for each individual optional field. Seems like a lot of tests, but isn't many small tests better than one large test?
Insight appreciated!
Upvotes: 3
Views: 920
Reputation: 16091
I am the author of said book. I meant that test as more of an introduction to the Django ORM, rather than as a demonstration of best practice, and I try to explain that at the time, but I suppose some confusion was inevitable. I'll have a think about how I could present things differently.
In any case, if you skip along to a few chapters later in the book, I show how to simplify the test to something that's more best-practicey.
Whether or not you test basic Django models is up to you -- some people will say testing a declarative syntax is over the top, others will say a short test is good to have as a placeholder. Here's one you might use:
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author)
ISBN = models.CharField(max_length=35)
abstract = models.TextField()
class BookTest(TestCase):
def test_defaults(self):
book = Book()
self.assertEqual(book.title, '')
self.assertEqual(book.author, None)
self.assertEqual(book.ISBN, '')
self.assertEqual(book.abstract, '')
So that's a placeholder. It encourages you to add more tests if you start introducing more complex fields like, say, a publication_date
field which has a default value of datetime.today() + one_month
, which might warrant a bit of testing to make sure you get it right. Having a placeholder lowers the barrier to subsequent tests. Other people will tell you that's over the top. You have to find your own balance.
One thing that's pretty widely accepted is that you should definitely test behaviour. So, if your model has a custom method:
class Book(models.Model):
# [...]
def is_available(self):
return self.pub_date < datetime.today() and Stock.objects.filter(book=self).count() > 0
Then some sort of test for that is definitely a good idea.
Upvotes: 4
Reputation: 599956
Why would you test the saving and loading of values to the database? That's Django's responsibility. Django has a whole suite of tests that check in detail that its database layer is working. There is absolutely no reason to check the basic behaviour of any of your fields, let alone all of them.
Your unit tests are for your logic: your custom methods, your views, your template tags. They are not for logic that is provided by Django by default.
Upvotes: 1
Reputation: 474161
This is where model factories would help a lot. There are two popular modules that provide it:
I've personally used factory_boy
and find it very easy to use.
Basically, you define a factory with default field values:
class UserFactory(factory.Factory):
class Meta:
model = models.User
first_name = 'John'
last_name = 'Doe'
admin = False
Then, you can use the factory and override field values if needed. It also supports Sequences, Lazy Attributes and other useful features for generating the data.
Speaking about your particular task, try not to test what django actually has tests for. For example, there is no need to test whether required
argument works. Test your custom model logic involved in manipulating the model.
Also, A Guide to Testing in Django
is a good read.
Hope that helps.
Upvotes: 1