如何在Django TestCase拆卸期间修复IntegrityErrors?

时间:2019-04-10 13:35:38

标签: django python-3.x pytest

我正在将一个大型Web应用程序从Python 2.7移植到Python 3.6。我已经管理了数据库迁移并使用2to3转换了代码,但是在运行测试套件时遇到了问题。对于大量测试,我会收到如下错误:

  62 self = <django.db.backends.utils.CursorWrapper object at 0x7fc7d0abbeb8>
  63 sql = 'SET CONSTRAINTS ALL IMMEDIATE', params = None
  64 
  65     def execute(self, sql, params=None):
  66         self.db.validate_no_broken_transaction()
  67         with self.db.wrap_database_errors:
  68             if params is None:
  69 >               return self.cursor.execute(sql)
  70 E               psycopg2.IntegrityError: insert or update on table "probex_historicalproject" violates forei
     gn key constraint "probex_historicalpro_history_user_id_88371c5c_fk_auth_user"
  71 E               DETAIL:  Key (history_user_id)=(303) is not present in table "auth_user".
  72 
  73 ../../venv3/lib/python3.6/site-packages/django/db/backends/utils.py:62: IntegrityError
  74 
  75 The above exception was the direct cause of the following exception:
  76 
  77 self = <django.test.testcases.TestCase testMethod=__init__>
  78 
  79     def _post_teardown(self):
  80         """Performs any post-test things. This includes:
  81 
  82             * Flushing the contents of the database, to leave a clean slate. If
  83               the class has an 'available_apps' attribute, post_migrate isn't fired.
  84             * Force-closing the connection, so the next test gets a clean cursor.
  85             """
  86         try:
  87 >           self._fixture_teardown()
  88 
  89 ../../venv3/lib/python3.6/site-packages/django/test/testcases.py:925:
  90 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
  91 ../../venv3/lib/python3.6/site-packages/django/test/testcases.py:1081: in _fixture_teardown
  92     connections[db_name].check_constraints()
  93 ../../venv3/lib/python3.6/site-packages/django/db/backends/postgresql/base.py:243: in check_constraints
  94     self.cursor().execute('SET CONSTRAINTS ALL IMMEDIATE')
  95 ../../venv3/lib/python3.6/site-packages/raven/contrib/django/client.py:127: in execute
  96     return real_execute(self, sql, params)
  97 ../../venv3/lib/python3.6/site-packages/django/db/backends/utils.py:64: in execute                          
  98     return self.cursor.execute(sql, params)                                                                 
  99 ../../venv3/lib/python3.6/site-packages/django/db/utils.py:94: in __exit__                                  
 100     six.reraise(dj_exc_type, dj_exc_value, traceback)                                                       
 101 ../../venv3/lib/python3.6/site-packages/django/utils/six.py:685: in reraise
 102     raise value.with_traceback(tb)
 103 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

我尝试在同事的某些机器上运行测试,有些人在运行测试时没有问题,但是有些人和我自己有相同的问题。

也值得注意:如果我自己运行单个测试文件(即py.test /path/to/file),则不会发生此错误。我认为它必须与测试之间处理数据库数据的方式有关。

这是一个失败的示例测试:

  1 
  2 from django.utils import timezone
  3 from tests.tests_probex.tests_python.testing_utilities import LoggedInTestCase
  4 from tests.tests_probex.tests_python.factories import JobFactory
  5 from probex.models import Profile, Tenant, Job
  6 from probex.views.reports import build_tenant_report
  7 from django.contrib.auth.models import User
  8 import pytest
  9 
 10 pytestmark = pytest.mark.xfail(reason="Not working in Python 3.6")
 11                                                                                                                             
 12                                                                                                                             
 13 class TestTenantReport(LoggedInTestCase):                                                                                   
 14     def setUp(self):                                                                                                        
 15         super(TestTenantReport, self).setUp()                                                                               
 16         self.end_date = timezone.now()                                                                                      
 17         self.delta = timezone.timedelta(days=7)                                                                             
 18         self.start_date = self.end_date - self.delta                                                                        
 19                                                                                                                             
 20         self.date = timezone.now() - timezone.timedelta(days=3)                                                             
 21         self.job = JobFactory(absolute_num=1600, relative_num=1600, protocol=self.protocol, project=self.project, status='Co
    mplete', created=self.date, submitter_id=self.user.id)                                                                      
 22                                                                                                                             
 23     def test_returns_new_users(self):                                                                                       
 24         users = User.objects.all()                                                                                          
 25         for user in users:                                                                                                  
 26             user.date_joined = self.date                                                                                    
 27             p = Profile.objects.get(user_id=user.id)                                                                        
 28             tenant = Tenant.objects.get(id=p.tenant_id)                                                                     
 29             tenant.app_scientist = self.user                                                                                
 30             user.save()                                                                                                     
 31             tenant.save()                                                                                                   
 32                                                                                                                             
 33         report = build_tenant_report(self.user)                                                                             
 34         self.assertGreaterEqual(report['new_users'].count(), users.count()) 

LoggedInTestCase是Django TestCase的子类:

 15 class LoggedInTestCase(TestCase):
 16     def setUp(self):
 17         self.user = User.objects.create_user(username='dummy@hotmail.com', email='dummy@hotmail.com', password="secret123")
 18         self.user.user_permissions.add(Permission.objects.get(codename='add_job'))
 19         self.user.user_permissions.add(Permission.objects.get(codename='change_job'))
 20         self.user.user_permissions.add(Permission.objects.get(codename='add_project'))
 21         self.user.user_permissions.add(Permission.objects.get(codename='add_user'))
 22         self.user.user_permissions.add(Permission.objects.get(codename='change_user'))                                                               
 23         self.tenant = Tenant.objects.create(name="testtenant")                                                                                       
 24         self.user.profile.tenant = self.tenant                                                                                                       
 25         self.user.profile.save()                                                                                                                     
 26         self.project = ProjectFactory(name="testproject", tenant=self.tenant, active=True, identifier='AAA')                                         
 27         self.protocol = ProtocolFactory()                                                                                                            
 28         self.root_term = TermFactory(is_root=True, acc='all')                                                                                        
 29         self.login()                                                                                                                                 
 30                                                                                                                                                      
 31     def login(self):                                                                                                                                 
 32         self.client.login(username='dummy@hotmail.com', password='secret123')                                                                        
 33                                                                                                                                                      
 34     def logout(self):                                                                                                                                
 35         self.client.logout()                                                                                                                         
 36                                                                                                                                                      
 37     def get_user(self):                                                                                                                              
 38         return self.client                                                                                                                           
 39                                                                                                                                                      
 40     def permission_restricted(self, response, first_stop_url):                                                                                       
 41         first_redirect, status_1 = response.redirect_chain[0]                                                                                        
 42         second_redirect, status_2 = response.redirect_chain[1]                                                                                       
 43         self.assertEqual(first_redirect, first_stop_url)                                                                                             
 44         self.assertEqual(status_1, 302)                                                                                                              
 45         self.assertEqual(second_redirect, reverse('home'))                                                                                           
 46         self.assertEqual(status_2, 302)   

这些测试过去在Python 2.7下运行时可以通过,除了更新到3.6以外,我没有做太多改变。我仍然希望他们现在通过。如果有人有任何指点,我将不胜感激。这里是第一次海报。

2 个答案:

答案 0 :(得分:0)

对于任何偶然发现此问题的人:提示是在违反的键中。 historicalprojecthistory_userdjango-simple-history包相关。我们最终将工厂男孩生成器更新为还包括历史工厂。这样就解决了问题。

答案 1 :(得分:0)

我在使用 django-simple-history 库时也遇到了这个问题。事实证明,我们的问题是由于多个模型在不同的数据库中相互依赖造成的。为了测试,我们更改了数据库路由器以将所有模型设置为在同一数据库中创建,这为我们解决了 IntegrityError。我认为在不同的数据库中拥有历史和模型也可能是一个问题。