5

So this seems to be an issue talked about here and there on StackOverflow with no real solution. So I have a bunch of tests that all pass when run individual. They even pass when run as a full test suite, EXCEPT when I add in my TestCase ExploreFeedTest. Now ExploreFeedTest passes when run by itself and it actually doesn't fail when run in the full test suite as in running python manage.py test, it causes another test HomeTest to fail, which passes on it's own and passes when ExploreFeedTest is commented out from the init.py under the test folder. I hear this is an issue with Django not cleaning up data properly? All my TestCase classes are from django.test.TestCase, because apparently if you don't use that class Django doesn't teardown the data properly, so I don't really know how to solve this. I'm also running Django 3.2.9, which is supposedly the latest. Anyone have a solution for this?

ExploreFeedTest.py

from django.test import TestCase
from django.urls import reverse
from rest_framework import status    

class ExploreFeedTest(TestCase):

Folder setup

enter image description here

Here are some others having similar issue:

here's a small snippet of the failing test I am getting

.............................FE.E.E.EFE.E.E.EFEFE.E.E.E.E.E.E.E.E.E.E..............................
======================================================================
ERROR: test_all_posts_contains_post_by_user_followees_and_follow_goal (cheers.test.PostTests.HomeTest.HomeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\envs\cheers\lib\site-packages\django\db\backends\utils.py", line 82, in _execute
    return self.cursor.execute(sql)
psycopg2.errors.ForeignKeyViolation: insert or update on table "cheers_post" violates foreign key constraint "cheers_post_join_goal_id_da1e6957_fk_cheers_joingoal_uuid"
DETAIL:  Key (join_goal_id)=(0947b806-f9f8-4e2c-98df-6072eaa61533) is not present in table "cheers_joingoal".

All the E I'm getting in the tests are essentially because of this. Keep in mind these all pass just fine when I comment out ExploreFeedTest and ExploreFeedTest passes successfully when ran alone. Also as you can see it's HomeTest failing.

Here's an example of one of the tests that's failing due to this error as well

class HomeTest(TestCase):
    @classmethod
    # Generates Test DB data to persist throughout all tests
    def setUpTestData(cls) -> None:
        cls.generate_count = 4
        cls.access_token = get_test_user_access_token()
        cls.num_posts = 0
        cls.user = create_test_user_in_DB()
        GoalCategory.objects.create(name='health', emoji_url='url')
        cls.goal = GoalFactory()
        cls.join_goal = JoinGoal.objects.create(joiner=cls.user, goal=cls.goal)

        # Posts by the current user
        for i in range(cls.generate_count):
            cls.num_posts += 1
            PostFactory()

        cls.followee = User.objects.create(uuid=uuid.uuid4(), username='Test')
        followee_join_goal = JoinGoal.objects.create(joiner=cls.followee, goal=cls.goal)
        FollowUser.objects.create(followee=cls.followee, follower=cls.user)
        # Posts by user the current user is following
        for i in range(cls.generate_count):
            cls.num_posts += 1
            Post.objects.create(creator=cls.followee, join_goal=followee_join_goal,
                                type=PostType.UPDATE, body='test')

        random_user = User.objects.create(uuid=uuid.uuid4(), username='Random')
        cls.followed_join_goal = JoinGoal.objects.create(joiner=random_user, goal=cls.goal)
        FollowGoal.objects.create(follower=cls.user, join_goal=cls.followed_join_goal)
        # Posts of goal current user is following
        for i in range(cls.generate_count):
            cls.num_posts += 1
            Post.objects.create(creator=random_user, join_goal=cls.followed_join_goal,
                                type=PostType.UPDATE, body='test')

        cls.count = int(cls.num_posts / 2) - 1
        cls.post_list = list(Post.objects.all().order_by('uuid'))
        cls.mid_idx, cls.mid_post = get_mid_idx_and_post_from_post_list(cls.post_list)

def test_all_posts_contains_post_by_user_followees_and_follow_goal(self):
    response = self.client.get(reverse('get_initial_home_feed',
                                       kwargs={'count': self.num_posts}),
                               **{'HTTP_AUTHORIZATION': f'bearer {self.access_token}'})
    self.assertEqual(response.status_code, status.HTTP_200_OK)
    self.assertEqual(len(response.data), self.num_posts)
    posts_by_user = list(Post.objects.filter(creator=self.user).values_list('uuid', flat=True))
    posts_by_followees = list(Post.objects.filter(creator=self.followee).values_list('uuid', flat=True))
    posts_of_followed_join_goal = list(
        Post.objects.filter(join_goal=self.followed_join_goal).values_list('uuid', flat=True))
    uuids_of_posts = [uuid.UUID(data['uuid']) for data in list(response.data)]
    self.assertTrue(all(uuid in uuids_of_posts for uuid in posts_by_user))
    self.assertTrue(all(uuid in uuids_of_posts for uuid in posts_by_followees))
    self.assertTrue(all(uuid in uuids_of_posts for uuid in posts_of_followed_join_goal))

Solutions I've tried so far:

  • Change ordering of tests (ExploreFeedTest started failing and HomeTest all succeeded when I did this
  • Can you share the failing test and where it's failing? – Brian Destura Nov 25 '21 at 04:53
  • @BrianDestura are you sure? It's very long. I have about 70 tests and 20 fail when I added in `ExploreFeedTest` –  Nov 25 '21 at 05:07
  • Maybe just a small part of the failing assertion? – Brian Destura Nov 25 '21 at 06:08
  • @BrianDestura added –  Nov 25 '21 at 08:33
  • Have you tried it on another machine and or environment? Could be a Heisenbug related to environment? Also, have you tried switching the order in which the "failing" test appears in the sequence? Will it pass that way? – MadPhysicist Nov 27 '21 at 05:58
  • I for one had a similar issue with Django. Turned out to be checking the id- when the transaction rolls back in the test and deletes the test data, it doesn’t reset the id counter, so when the next test creates data, the ids continue from where they were. Also sometimes Django just wouldn’t delete test data. If that’s the case when you debug the test- Fill out a ticket in Django. Try to debug the test and see what is the data. – Lior Pollak Nov 27 '21 at 06:00
  • 1
    P.S. I see that the test has to do with DB testing. The failure during the collective run may have something to do with the DB state, as affected by the tests preceding the one in question. – MadPhysicist Nov 27 '21 at 06:00
  • @MadPhysicist how do you change the order that TestCase is run? –  Nov 27 '21 at 08:27
  • @MadPhysicist https://stackoverflow.com/questions/2581005/django-testcase-testing-order/2581056 –  Nov 27 '21 at 08:38
  • @MadPhysicist I changed the ordered and it looks like my `ExploreFeedTest` failed now. –  Nov 27 '21 at 08:39
  • perhaps there is a way to force teardown again? As in clear the DB for sure at the end of test? Would that be a solution? –  Nov 27 '21 at 08:40
  • 1
    @user8714896 It looks like there is some test interaction and interdependence going on. That should not be happening. I am not sure whether this is because of the way you set up the DB in the tests or it is a Django bug. Logically, it could be either. That is my guess. – MadPhysicist Nov 27 '21 at 16:15
  • @MadPhysicist my theory is Django seeing as this has been a known issue for a bit. `ExploreFeedTest` and `HomeTest` are separate TestCase classes therefore a fresh test DB should be booted up for each. –  Nov 27 '21 at 21:29
  • @user8714896 Is there a bug tracker on this? How do you surmise it is a known issue? – MadPhysicist Nov 28 '21 at 05:15
  • @MadPhysicist I was able to find a bunch of StackOverflow and other such community posts on the subject and no one seems to be able to find a solution to it. The general consensus I've read is that it's an internal bug. I've referenced some of those posts as well in my original post. –  Nov 28 '21 at 05:16
  • In cases like this, I use PyCharm to debug into my tests. I'll run the entire test suite with a break point in the test that I know fails. One potential issue I see is the `HomeTest.setUpTestData()` method. If one test modify the data that is set up here but another test expects the data in its original form, then taht will cause issues. I always write tests classes with a `setUp()` method that creates data specifically for the tests in that class. That way no test will cause another test to fail due to modified data. Also, I always assign ids explicitly, especially if I use them in an assert. – Code-Apprentice Nov 28 '21 at 07:23
  • @user8714896 Is there any reference to a line of code from your own project in the error stack trace? – Code-Apprentice Nov 28 '21 at 07:32
  • @Code-Apprentice yes if you look at the original post I refer to the line in `setupTestData()` that's causing the error. I don't know if there issue is coming from tests in `HomeTest` breaking each other. The reason I think this is because it runs fine when run alone, but not with `ExploreFeedTest`. This tells me something is going wrong when taking down the test DB. When I switch the order of tests `ExploreFeedTest` fails, but individually they're fine. They're both TestCase so they should be running independently i.e data once existed for one of them should not exist at all for the other. –  Nov 28 '21 at 10:10
  • 1
    Read [How to create a Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). – aaron Nov 28 '21 at 10:21
  • @user8714896 I don't think there is a general solution to this kind of problem. You will need to debug further to find the problem in your specific case. I suggest starting by setting a break point in your code at the line which causes the foreign key constraint. Then work backwards from there. – Code-Apprentice Nov 28 '21 at 15:17
  • Maybe https://stackoverflow.com/questions/30658491/integrityerror-insert-or-update-on-table-orders-order-violates-foreign-key-con/30658538 will be useful. – Code-Apprentice Nov 28 '21 at 15:19
  • I'm willing to bet that one of your tests deletes an object that is involved in a foreign key relationship. This is exactly why it is best to setup data for each test. Not for each test class, but for each test. – Code-Apprentice Nov 28 '21 at 15:24
  • @Code-Apprentice if that was the case then the TestCase run individually would fail as well wouldn't it? –  Nov 28 '21 at 20:09
  • @user8714896 not necessarily. What I mean is that TestCase "A" deletes an object. Later TestCase "B" tries to add that object as a foreign key to another object. But since the first object no longer exists in the database TestCase "B" fails. When TestCase "B" runs alone, it passes because the object used for its foreign key is created normally during setup. This kind of reusing objects in the database is a big no-no for making robust test cases. – Code-Apprentice Nov 29 '21 at 15:30
  • @Code-Apprentice O I see. Yea I don't believe I'm doing that because I set my `setupTestData` functions to be independent of other TestCase data setups –  Nov 30 '21 at 02:31
  • @user8714896 You need to debug your tests to verify that assumption or otherwise find what the problem is. – Code-Apprentice Nov 30 '21 at 07:50
  • @user8714896 Did you make sure that database is being cleared after every test? When using pytest-django this is done automatically for you, however it doesn't seem that you're using pytest. – Daniel Micallef Nov 30 '21 at 14:29
  • @DanielMicallef how do I ensure that? What's pytest-django? –  Nov 30 '21 at 22:45
  • Just to be sure. Replace your class method that makes the database available through al tests to be a method that runs in every test during set up. That way you can roll out it's not a database issue if it's still happens. – Gabo Dec 01 '21 at 21:47
  • @Gabo how do I do that? –  Dec 02 '21 at 03:31
  • wrote answer at this post: https://stackoverflow.com/questions/70166752/django-serializer-throwing-invalid-pk-object-does-not-exist-when-setting-m –  Dec 11 '21 at 12:12

1 Answers1

0

I posted the answer on the stack overflow question

Django - Serializer throwing "Invalid pk - object does not exist" when setting ManyToMany attribute where foreign keyed object does exist

I was also using factory boy, which doesn't seem to play nice with test suite. Test suite doesn't seem to know how to rollback the DB without getting rid of factory boy generated data.