3

I've replaced my auth.user model with a custom user model whose username is unique_together with the company. To make this work, I remove the unique constraint from the user table manually (via a migration). I'd like to write a test to verify that this works as expected (returns error messages when appropriate, etc.), but I'm having trouble understanding why the test framework isn't behaving like I'd expect. Here's what I'm doing in the test:

class UserTests(TestCase):
    def setUp(self):
        self.client = Client()
        # Setup: Create users, companies, etc. to use in the test

    def test_create_dup_staffuser(self):
        cursor = connection.cursor()
        try:
            cursor.execute("alter table config_user drop index `username`;")
        finally:
            cursor.close()

        # Test code: Create some data, pass it to my 'create user' view,
        # verify the results

    def test_user_something_else(self):
        # Test other things        

The test itself works. The constraint is removed as expected, and my test passes, allowing me to create users with duplicate usernames within a company. The problem is, the constraint removal prevents my database from getting refreshed prior to the next test and test_user_something_else starts off with all the data still leftover from test_create_dup_staffuser (I verified this by commenting out the alter table, which enables everything to refresh).

I suspect from stepping down into TestCase that my change is preventing the transaction rollback at the end of the test somehow, but I'm not really sure how yet or what to do about it. I tried putting the constraint back at the end of my test, but got the same result. I also tried adding a tearDown and deleting all my data from my database manually, but that didn't work either. When I hit my breakpoint at the beginning of setUp on the next test, the data was back.

Can someone explain what's going on here and suggest how I might reasonably test this functionality? My next step will probably be to turn on database logging to try to figure out what's happening, but other than that, I'm out of ideas.

Workarounds I've already considered:

  • Use migrations: Our migrations are complicated and have some errors. Fixing/squashing them is on the list, but not yet.
  • Rework the class: I can (now) think of all sorts of ways to change my custom user class to make this less complicated, but this code's already in production and working, so I can't really do that right now, either.
  • Upgrade to django 1.7: It's already on the list, but won't make it into this development cycle.
  • Just skip that test for now: This is my current backup plan.

Using django 1.5.8/python 2.7/mysql 5.5

Ennael
  • 861
  • 3
  • 14
  • 31

1 Answers1

3

I suspect from stepping down into TestCase that my change is preventing the transaction rollback at the end of the test somehow, but I'm not really sure how yet or what to do about it.

I think that's it. I would suggest moving to TransactionTestCase, which does not use transactions for resetting the database.

Why would transactions be messed up in your case? Bearing in mind that these things keep changing, this answer (by user joeforker) indicates that MySQL, which you use, treats instructions that alter schemas as "caus[ing] an implicit commit", which would definitely mess up what TestCase expects. So your alter table command would "pull the rug" (so to speak) from under the feet of TestCase.

Community
  • 1
  • 1
Louis
  • 146,715
  • 28
  • 274
  • 320
  • Aha, that makes perfect sense. I didn't realize you could use that class directly. When I derive my test class from TransactionTestCase instead of TestCase it works as expected. Thanks!! – Ennael Oct 15 '14 at 17:01