python django unit-testing django-signals

python - TransactionManagementError "No se pueden ejecutar consultas hasta el final del bloque ''atómico''" mientras se utilizan señales, pero solo durante la prueba de la unidad



django unit-testing (5)

Como @mkoistinen nunca hizo su comentario , una respuesta, publicaré su sugerencia para que la gente no tenga que buscar comentarios.

considere simplemente declarar su clase de prueba como una TransactionTestCase en lugar de simplemente TestCase.

De los docs : una TransactionTestCase puede invocar commit y rollback y observar los efectos de estas llamadas en la base de datos.

Estoy obteniendo TransactionManagementError al intentar guardar una instancia de modelo de usuario de Django y en su señal post_save, estoy guardando algunos modelos que tienen al usuario como clave externa.

El contexto y el error son bastante similares a esta pregunta django TransactionManagementError al usar señales

Sin embargo, en este caso, el error ocurre solo durante la prueba unitaria .

Funciona bien en pruebas manuales, pero las pruebas unitarias fallan.

¿Hay algo que me estoy perdiendo?

Aquí están los fragmentos de código:

views.py

@csrf_exempt def mobileRegister(request): if request.method == ''GET'': response = {"error": "GET request not accepted!!"} return HttpResponse(json.dumps(response), content_type="application/json",status=500) elif request.method == ''POST'': postdata = json.loads(request.body) try: # Get POST data which is to be used to save the user username = postdata.get(''phone'') password = postdata.get(''password'') email = postdata.get(''email'',"") first_name = postdata.get(''first_name'',"") last_name = postdata.get(''last_name'',"") user = User(username=username, email=email, first_name=first_name, last_name=last_name) user._company = postdata.get(''company'',None) user._country_code = postdata.get(''country_code'',"+91") user.is_verified=True user._gcm_reg_id = postdata.get(''reg_id'',None) user._gcm_device_id = postdata.get(''device_id'',None) # Set Password for the user user.set_password(password) # Save the user user.save()

signal.py

def create_user_profile(sender, instance, created, **kwargs): if created: company = None companycontact = None try: # Try to make userprofile with company and country code provided user = User.objects.get(id=instance.id) rand_pass = random.randint(1000, 9999) company = Company.objects.get_or_create(name=instance._company,user=user) companycontact = CompanyContact.objects.get_or_create(contact_type="Owner",company=company,contact_number=instance.username) profile = UserProfile.objects.get_or_create(user=instance,phone=instance.username,verification_code=rand_pass,company=company,country_code=instance._country_code) gcmDevice = GCMDevice.objects.create(registration_id=instance._gcm_reg_id,device_id=instance._gcm_reg_id,user=instance) except Exception, e: pass

tests.py

class AuthTestCase(TestCase): fixtures = [''nextgencatalogs/fixtures.json''] def setUp(self): self.user_data={ "phone":"0000000000", "password":"123", "first_name":"Gaurav", "last_name":"Toshniwal" } def test_registration_api_get(self): response = self.client.get("/mobileRegister/") self.assertEqual(response.status_code,500) def test_registration_api_post(self): response = self.client.post(path="/mobileRegister/", data=json.dumps(self.user_data), content_type="application/json") self.assertEqual(response.status_code,201) self.user_data[''username'']=self.user_data[''phone''] user = User.objects.get(username=self.user_data[''username'']) # Check if the company was created company = Company.objects.get(user__username=self.user_data[''phone'']) self.assertIsInstance(company,Company) # Check if the owner''s contact is the same as the user''s phone number company_contact = CompanyContact.objects.get(company=company,contact_type="owner") self.assertEqual(user.username,company_contact[0].contact_number)

Rastrear:

====================================================================== ERROR: test_registration_api_post (nextgencatalogs.apps.catalogsapp.tests.AuthTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/nextgencatalogs/apps/catalogsapp/tests.py", line 29, in test_registration_api_post user = User.objects.get(username=self.user_data[''username'']) File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/manager.py", line 151, in get return self.get_queryset().get(*args, **kwargs) File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/query.py", line 301, in get num = len(clone) File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/query.py", line 77, in __len__ self._fetch_all() File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/query.py", line 854, in _fetch_all self._result_cache = list(self.iterator()) File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/query.py", line 220, in iterator for row in compiler.results_iter(): File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 710, in results_iter for rows in self.execute_sql(MULTI): File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 781, in execute_sql cursor.execute(sql, params) File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/backends/util.py", line 47, in execute self.db.validate_no_broken_transaction() File "/Users/gauravtoshniwal1989/Developer/Web/Server/ngc/ngcvenv/lib/python2.7/site-packages/django/db/backends/__init__.py", line 365, in validate_no_broken_transaction "An error occurred in the current transaction. You can''t " TransactionManagementError: An error occurred in the current transaction. You can''t execute queries until the end of the ''atomic'' block. ----------------------------------------------------------------------


Estaba obteniendo este error al ejecutar pruebas unitarias en mi función create_test_data usando django 1.9.7. Funcionó en versiones anteriores de django.

Se veía así:

cls.localauth,_ = Organisation.objects.get_or_create(organisation_type=cls.orgtypeLA, name=''LA for test'', email_general=''[email protected]'', address=''test'', postcode=''test'', telephone=''test'') cls.chamber,_ = Organisation.objects.get_or_create(organisation_type=cls.orgtypeC, name=''chamber for test'', email_general=''[email protected]'', address=''test'', postcode=''test'', telephone=''test'') cls.lawfirm,_ = Organisation.objects.get_or_create(organisation_type=cls.orgtypeL, name=''lawfirm for test'', email_general=''[email protected]'', address=''test'', postcode=''test'', telephone=''test'') cls.chamber.active = True cls.chamber.save() cls.localauth.active = True cls.localauth.save() <---- error here cls.lawfirm.active = True cls.lawfirm.save()

Mi solución fue usar update_or_create en su lugar:

cls.localauth,_ = Organisation.objects.update_or_create(organisation_type=cls.orgtypeLA, name=''LA for test'', email_general=''[email protected]'', address=''test'', postcode=''test'', telephone=''test'', defaults={''active'': True}) cls.chamber,_ = Organisation.objects.update_or_create(organisation_type=cls.orgtypeC, name=''chamber for test'', email_general=''[email protected]'', address=''test'', postcode=''test'', telephone=''test'', defaults={''active'': True}) cls.lawfirm,_ = Organisation.objects.update_or_create(organisation_type=cls.orgtypeL, name=''lawfirm for test'', email_general=''[email protected]'', address=''test'', postcode=''test'', telephone=''test'', defaults={''active'': True})


Me encontré con este mismo problema yo mismo. Esto se debe a una peculiaridad en la forma en que se manejan las transacciones en las versiones más nuevas de Django, junto con una prueba unitaria que activa intencionalmente una excepción.

Tuve una prueba unitaria que verificaba para asegurarme de que se aplicaba una restricción de columna única activando deliberadamente una excepción IntegrityError:

def test_constraint(self): try: # Duplicates should be prevented. models.Question.objects.create(domain=self.domain, slug=''barks'') self.fail(''Duplicate question allowed.'') except IntegrityError: pass do_more_model_stuff()

En Django 1.4, esto funciona bien. Sin embargo, en Django 1.5 / 1.6, cada prueba se envuelve en una transacción, por lo que si se produce una excepción, se rompe la transacción hasta que la retrotraiga explícitamente. Por lo tanto, cualquier operación de ORM adicional en esa transacción, como mi do_more_model_stuff() , fallará con esa excepción django.db.transaction.TransactionManagementError .

Al igual que caio mencionado en los comentarios, la solución es capturar su excepción con transaction.atomic como:

from django.db import transaction def test_constraint(self): try: # Duplicates should be prevented. with transaction.atomic(): models.Question.objects.create(domain=self.domain, slug=''barks'') self.fail(''Duplicate question allowed.'') except IntegrityError: pass

Eso evitará que la excepción intencionalmente lanzada rompa la transacción de la prueba unitaria.


Tengo el mismo problema, pero with transaction.atomic() y TransactionTestCase no funcionó para mí.

python manage.py test -r lugar de python manage.py test está bien para mí, tal vez el orden de ejecución es crucial

luego encuentro un documento sobre el pedido en el que se ejecutan las pruebas , menciona qué prueba se ejecutará primero.

Entonces, uso TestCase para la interacción con la base de datos, unittest.TestCase para otra prueba simple, ¡funciona ahora!


Tuve el mismo problema.

En mi caso, yo estaba haciendo esto

author.tasks.add(tasks)

así que convertirlo a

author.tasks.add(*tasks)

Eliminado ese error.