1

I have a somewhat large Django project with many views/models. I recently migrated my project from sqlite3 to postgres locally, and plan to scale it out even further (put postgres on a separate machine, etc).

I followed these instructions when I migrated from sqlite to postgres, and it appears to have worked perfectly. (ie, my running app looks identical to when the db was sqlite)

My issue is this: When I run my previously-written unittests, the first unittest works and all proceeding unittests fail. Individually, the unittests work fine. I've seen some other posts on stackoverflow that addressed this issue, but the solutions were so unclear. How can I rework my setUp()/ teardown() methods for my unittests so that they will pass with my newly migrated postgres db? Do i need to completely rewrite all unittests?

I've seen the pytest-postgresql library, although I'm not entirely sure how to modify my unittests based off of this.

My testing suite is set up with different classes that test views. So for example,

class View1Tests(TestCase):
    def setUp(self):
        c1 = Category.objects.create(id=55555, leaf_node_name="Test Category 1")
        c2 = Category.objects.create(id=12345, leaf_node_name="Test Category 2")

        s1 = Search.objects.create(category=c1, username="testuser")
        s2 = Search.objects.create(category=c2, username="testuser2")


    def test_view1_success(self):
         #blablabla

    def test_view1_fail(self):
         #blablabla

    def test_view1_something(self):
         #blablabla

I'm getting errors like this:

appname.models.DoesNotExist: Search matching query does not exist.

Again, all of these unittests run perfectly fine when sqlite3 was the db. I think its an issue with the postgres testing settings? But I'm at a loss as to where to even begin. Any help would be appreciated!!

M.javid
  • 6,387
  • 3
  • 41
  • 56
  • Possibly a silly question, but presumably your test classes extend `django.test.TestCase` and you're running your tests with `python manage.py test`? – Will Keeling Oct 09 '18 at 16:51
  • @WillKeeling my tests extend django.test.TestCase. However, I am running `pytest` to run all the tests. Running `python manage.py test` also throws identical errors though. – Heidi Lyons Oct 09 '18 at 17:16
  • @HeidiLyons: did you manage to solve this problem? I'm in the same spot now. Would be great if you could post what worked for you! Thanks. – kchomski Sep 05 '19 at 12:01
  • I'm getting this same error when moving from sqlite to postgresql. I'll post a reply if I ever figure out what the issue is – Scratch'N'Purr Dec 31 '19 at 15:58
  • I think I found the solution. Solution is posted below. – Scratch'N'Purr Dec 31 '19 at 19:27

1 Answers1

1

I think I finally figured out the problem. The issue arises when trying to fetch an object by the default primary key (e.g. MyModel.objects.get(pk=1), MyModel.objects.get(id=1)). My assumption is that Postgres uses a serial data type as the AutoField and the value of the increment depends on the sequence generated by Postgres.

The solution I found is really simple. Either fetch by an attribute that has a unique property for that object, or fetch all objects and filter using list indexing. The latter method shouldn't be too cumbersome since in most cases, a few instances of the model are created.

Here's a full example

# models.py
class Book(models.Model):
    isbn = models.CharField(
        max_length=13,
        primary_key=True  # uniqueness is enforced
    )
    book_name = models.CharField(
        max_length=128,
        unique=True  # another unique field
    )
    author_count = models.IntegerField()
    book_genre = models.CharField(max_length=64)


# tests.py
class MyTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        # first object
        Book.objects.create(
            isbn='10101',
            book_name='Binary Code',
            author_count=1,
            book_genre='Fiction'
        )

        # second object
        Book.objects.create(
            isbn='314159',
            book_name='Life of Pi',
            author_count=1,
            book_genre='Philosophy'
        )

    def setUp(self):
        self.book1 = Book.objects.all()[0]
        # or self.book1 = Book.objects.get(isbn='10101')
        # or self.book1 = Book.objects.get(book_name='Binary Code')
        self.book2 = Book.objects.all()[1]
        # or self.book2 = Book.objects.get(isbn='314159')
        # or self.book2 = Book.objects.get(book_name='Life of Pi')

    def test_something(self):
        # lastly, since `setUp` is called before every test_* method
        # you can just access the instance's attributes without calling `get`
        # e.g:
        self.assertEqual(self.book1.book_genre, 'Fiction')

    def test_something_else(self):
        # you an also reload the instance using `refresh_from_db`
        # after making changes
        Book.objects.filter(author_count=1).update(book_genre='Fiction')
        self.book2.refresh_from_db()
        self.assertEqual(self.book2.book_genre, 'Fiction')
Scratch'N'Purr
  • 9,959
  • 2
  • 35
  • 51
  • Why does this happen though? – Goutam B Seervi Aug 04 '20 at 09:26
  • I have a question... I want to test PATCH and DELETE methods which require the id of the model to be provided. How do I test for this, the id's are kinda random. Previously, I was using sqlite but then after migrating to postgres, these tests started to fail. – Goutam B Seervi Aug 04 '20 at 10:00
  • @GoutamBSeervi as mentioned in my answer, use list indexing to grab the records as opposed to fetching by ID. This should allow you to test the other methods. – Scratch'N'Purr Aug 04 '20 at 13:41
  • That's a lot of work.. I'll have to rewrite all my tests. I have been stuck on this error for a day now – Goutam B Seervi Aug 04 '20 at 13:46
  • I was able to solve this by replacing TestCase with TransactionTestCase, now my tests work fine...but they take more time now. xD It used to take around a second earlier..now it's 70 seconds – Goutam B Seervi Aug 05 '20 at 06:50
  • That makes sense considering TransactionTestCases will have the db commit and then rollback after each test. Thanks for sharing. – Scratch'N'Purr Aug 06 '20 at 07:47