Nam Ngo's blog

Musings of a Software Developer.

Burn Your Fixtures, Use Model Factories

This blog post is inspired by one of the talks in PyCon US 2012 by Carl Meyer named Testing and Django in which he discusssed the guidelines for writing good tests.

After watching the whole talk, I realised what I’ve been doing is all wrong. The fact that tests I write run within minutes shows how terrible my unit-test-fu is. Using fixtures was a really bad idea. Each test case I wrote would load some fixtures to setup necessary dependencies. Sometimes, this creates a problem when the model has been redesigned. The fixtures would have to be edited due to the change of the model otherwise they won’t be installed properly.

Instead of using fixtures, Carl introduced model factories which would create a model instance with default values. Of course, you will have a flexibility to change those default values to suit each of your test because naturally, when you test a behaviour of a class, you definitely need it to be in different conditions.

Here is an example of a model factory that was used in the talk:

1
2
3
4
5
6
7
8
9
10
def create_profile(**kwargs):
    defaults = {
        "likes_cheese": True,
        "age": 32,
        "address": "3815 Brookside Dr",
    }
    defaults.update(kwargs)
    if "user" not in defaults:
        defaults["user"] = create_user()
    return Profile.objects.create(**defaults)

And this is how you could use it in the test:

1
2
3
4
def test_can_vote(self):
    """A user age 18+ can vote in the US."""
    profile = create_profile(age=18)
    self.assertTrue(profile.can_vote)

This approach solves the problem with maintaining the fixtures to suit your model design. If your model changes, the only thing you have to edit is the default values of the factory. Therefore model factories are way easier to maintain. If you need a large data set, you could easily write a for loop that wraps the model factory. So burn all your fixture right now !!!

Comments