Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test a Django South "datamigration"

I created a data migration using south, that takes a versions table and converts it from:

major: 1, minor: 2, micro: 3, release: a

into a simpler:

name: 1.2.3.a

Now I want to test this datamigration using django unit testing (1.3beta).

How can I use south programatically to roll migrations forward and backward while specifying custom fixtures to use that I can validate?

like image 553
Evgeny Avatar asked Jan 25 '11 16:01

Evgeny


2 Answers

I've asked this question on the Django South IRC but didn't really get an answer; they did make me question the "why" of unit testing data migrations (since it's usually a one time thing and you're not going to refactor it anyway, so you might as well do some manual checks).

However, I found 2 reasons to to "real testing":

  • Writing my assumptions down beforehand forces me to be explicit, hence more likely to be correct.
  • I can read about the assumptions in some place other than the actual code (which is complicated for a rather large datamigration)

In the end I simply decided on tacking on a number of assertions (i.e. regular python statement) at the end of the datamigration. This has the advantages mentioned above, and the additional advantage of doing a rollback if one of the assertions fails and telling you exactly what part of reality is not like you expected it to be.

like image 73
Klaas van Schelven Avatar answered Sep 17 '22 06:09

Klaas van Schelven


This isn't really a unit test: it's some other kind of test... Which means you'll probably have to look outside the normal unit-testing frameworks -- though of course you can use the existing tools to build what you want.

What I'd do is create a brand new test suite away from my normal django tests, and define an attribute in each test that defines its "lifespan": the first and last migrations for which you expect it to pass.

Then, write a script that basically does this:

for m in range(latestMigrationNumber):
    name = findNameOfMigrationNumber(m)   # look in the migrations directory
    executeMigration(name)                # os.system(), subprocess.*, etc
    runTheTests()

You can use a decorator to specify the "lifespan" for each test, perhaps by extending this "enable/disable" decorator concept to compare the current migration number (which you'd have to store globally somewhere) with the tests you expect to pass, and have it swap the pass/fail result (so if the test passes outside of its lifespan, the decorator makes it fail, and vice versa).

To test the backward migrations, just use the same scheme but run the loop backwards.

like image 42
tangentstorm Avatar answered Sep 18 '22 06:09

tangentstorm