In my last article, I provided an overview of testing in Django, including some guidelines and case studies. This time, I’ll demonstrate a more involved example and provide a tool called Model Mama for generating test data.
Just why Should You Give a Crap?
In my last article, I said that in lieu of fixtures and the ORM, we may utilise “factory boy, model mommy, and mock” to generate test data. As your model changes, you’ll need to update the ORM and the fixtures, which might take a while.
In a nutshell, the following are all issues with the Django Testing Fixtures:
- must be updated each time your model/schema changes,
- are really, really slow; and
- sometimes hard-coded data can cause your tests to fail in the future.
Because of this, Model Mother may be used to make fixtures that load faster and need less upkeep over time.
Equipment for Testing in Django
Let’s have a look at our model testing example from the last section.
class WhateverTest(TestCase):
def create_whatever(self, title="only a test", body="yes, this is only a test"):
return Whatever.objects.create(
title=title, body=body, created_at=timezone.now())
def test_whatever_creation(self):
w = self.create_whatever()
self.assertTrue(isinstance(w, Whatever))
self.assertEqual(w.__unicode__(), w.title)
In this case, we just created an Anything() object and checked to see whether its title was what was required.
Launch the server and execute the tests if you have the Project from the repository installation:
You may be certain that the aforementioned checks are valid, since:
test_whatever_creation (whatever.tests.WhateverTest) ... ok
Model Mother allows us to avoid the tedious task of repeatedly creating a new instance with the same set of attributes.
Maternal Role Model
Install:
$ pip install model_mommy
Think you can recall our model’s appearance?
class Whatever(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.title
Our rewrite of the aforementioned evaluation is as follows:
from model_mommy import mommy
class WhateverTestMommy(TestCase):
def test_whatever_creation_mommy(self):
what = mommy.make(Whatever)
self.assertTrue(isinstance(what, Whatever))
self.assertEqual(what.__unicode__(), what.title)
Run it. Was there a success verdict?
Whoa, that’s quite simple. No need to pass in arguments.
Alternate Version
Let’s examine a somewhat more involved case.
Setup
Make a whole new app:
$ python manage.py startapp whatevs
In the settings.py file, add the app to the Installed Apps section.
Make the prototype:
from django.db import models
from django.contrib.auth.models import User
from django.contrib import admin
class Forum(models.Model):
title = models.CharField(max_length=100)
def __unicode__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
creator = models.ForeignKey(User, blank=True, null=True)
forum = models.ForeignKey(Forum)
body = models.TextField(max_length=10000)
def __unicode__(self):
return unicode(self.creator) + " - " + self.title
Get the database in sync.
How does our report on coverage look like?
Test
Add the tests:
from model_mommy import mommy
from django.test import TestCase
from whatevs.models import Forum, Thread
class WhateverTestMommy(TestCase):
def test_forum_creation_mommy(self):
new_forum = mommy.make('whatevs.Forum')
new_thread = mommy.make('whatevs.Thread')
self.assertTrue(isinstance(new_forum, Forum))
self.assertTrue(isinstance(new_thread, Thread))
self.assertEqual(new_forum.__unicode__(), new_forum.title)
self.assertEqual(
new_thread.__unicode__(),
(str(new_thread.forum) + " - " + str(new_thread.title)))
Re-run your tests (which should pass), then construct the coverage report:
$ coverage run manage.py test whatevs -v 2
$ coverage html
Well?
Care to try running the following tests using JSON fixtures to understand how how to put the tests up using the Django Testing Fixtures?
I’m not sure what we’ll have in store for the next lesson, so let me know what you’d want to see. This is the source code, feel free to use it. Make remember to comment below if you have questions. Cheers!
Sign off on it as done.