unitarias tests test running pruebas manage example create automated django unit-testing

tests - Las pruebas de Django se quejan de que faltan tablas



running tests django (3)

Dado que está usando una base de datos heredada, probablemente no esté agregando el nombre de la aplicación a INSTALLED_APPS. Si una aplicación no está incluida en INSTALLED_APPS, las tablas para los modelos de las aplicaciones no se crearán en syncdb. Esto funciona para usted en producción ya que ya tiene una tabla, pero no en un entorno de prueba.

Puede adoptar cualquiera de los siguientes:

  • La forma de supermonkeypatch: elimine app_name de la clase Meta del Cliente, coloque el modelo en un archivo models.py dentro de un mcif con el nombre del módulo python, y agregue mcif a INSTALLED_APPS, solo para realizar pruebas

  • La mejor manera: extienda DjangoTestSuiteRunner y anule setup_test_environment para llamar a super y luego cree su tabla heredada manualmente en la base de datos de prueba.

  • La mejor manera: ponga su modelo en el módulo de aplicación debidamente nombrado. Elimine app_name del modelo Meta pero agregue managed = False docs . Incluir el nombre de la aplicación en INSTALLED_APPS. Ahora django no creará una tabla para ese modelo. Luego use este buen fragmento de código que la gente del grupo Caktus ha compilado para ejecutar sus pruebas.

¡Aclamaciones!

Editar - Cómo usar el DjangoTestSuiteRunner anulado

Necesitarás al menos Django 1.2 para esto.

Copia el código desde aquí . Ponlo en utils.py dentro de la aplicación mcif.

Agrega / edita lo siguiente en settings.py:

TEST_RUNNER = ''mcif.utils.ManagedModelTestRunner''

Ahora, cuando ejecute las pruebas, todas las tablas no administradas se tratarán como tablas administradas solo durante la duración de la prueba. Así que las tablas se crearán antes de ejecutar las pruebas.

Observe esta parte del código, que es donde ocurre la magia.

self.unmanaged_models = [m for m in get_models() if not m._meta.managed] for m in self.unmanaged_models: m._meta.managed = True

2ª Edición: Posibles Gotchas

Asegúrese de lo siguiente:

  • El usuario de la base de datos tiene privilegios para crear bases de datos y no solo tablas porque django intentará crear una base de datos de prueba
  • Los casos de prueba extienden django.test.TransactionTestCase, ya que tiene un comportamiento transaccional
  • Si no se aplica ninguna de las situaciones anteriores, coloque un pdb en el entorno_prueba_testRunner de ManagedModelTest para asegurarse de que se está alcanzando el código. Porque si se alcanza ese código, la tabla debería crearse

Tercera edición: depuración Dentro de mcif.utils.ManagedModelTestRunner, reemplace la función setup_test_environment con lo siguiente y déjeme saber si la salida de su prueba cambia:

def setup_test_environment(self, *args, **kwargs): print "Loading ManagedModelTestRunner" from django.db.models.loading import get_models self.unmanaged_models = [m for m in get_models() if not m._meta.managed] for m in self.unmanaged_models: print "Modifying model %s to be managed for testing" % m m._meta.managed = True super(ManagedModelTestRunner, self).setup_test_environment(*args, **kwargs)

Cuando ejecuto mi prueba que trata con mi modelo de Customer , recibo el siguiente error:

DatabaseError: (1146, "Table ''test_mcif2.customer'' doesn''t exist")

No estoy del todo sorprendido porque tengo mi proyecto Django conectado a una base de datos "heredada". Ya que mis mesas no fueron creadas "a la manera de Django", no es sorprendente que Django no pueda hablar con ellas sin un poco de trabajo. Aquí está mi modelo:

from django.db import models from django.db import connection, transaction from mcif.models.mcif_model import McifModel class Customer(McifModel): class Meta: db_table = u''customer'' app_name = ''mcif'' id = models.BigIntegerField(primary_key=True) customer_number = models.CharField(unique=True, max_length=255) social_security_number = models.CharField(unique=True, max_length=33) name = models.CharField(unique=True, max_length=255) phone = models.CharField(unique=True, max_length=255) deceased = models.IntegerField(unique=True, null=True, blank=True) do_not_mail = models.IntegerField(null=True, blank=True) created_at = models.DateTimeField() updated_at = models.DateTimeField() def distinguishing_column_names(self): return [''name'', ''customer_number'', ''social_security_number'', ''phone'']

¿Alguna idea de por qué exactamente esto no está funcionando?

Edición: Aquí está McifModel :

from django.db import models from django.db import connection, transaction class McifModel(models.Model): class Meta: abstract = True def upsert(self): cursor = connection.cursor() cursor.execute(self.upsert_sql()) transaction.commit_unless_managed() return self def value_list(self): return '',''.join(map(lambda column_name: "''{c}''".format(c=getattr(self, column_name)), self.distinguishing_column_names())) def upsert_sql(self): column_names = '',''.join(self.distinguishing_column_names()) return "INSERT IGNORE INTO {t} ({c}) VALUES ({v})".format(t=self._meta.db_table, c=column_names, v=self.value_list()) @classmethod def save_from_row(cls, row): object = cls() map(lambda column_name: setattr(object, column_name, row.value(object._meta.db_table, column_name)), object.distinguishing_column_names()) return object.upsert()

Edición: Tomé el consejo de tarequeh y puse el contenido del archivo Caktus en mcif/utils.py . También configuré TEST_RUNNER = ''mcif.utils.ManagedModelTestRunner'' . Si voy a la consola puedo verificar que el Customer no está administrado:

>>> [m for m in get_models() if not m._meta.managed] [<class ''mcif.models.customer.Customer''>]

Sin embargo, mi prueba todavía se queja de que la tabla no existe. ¿Qué me estoy perdiendo?

Aquí está mi settings.py:

# Django settings for mcifdjango project. DEBUG = True TEMPLATE_DEBUG = DEBUG ADMINS = ( (''Jason Swett'', ''[email protected]''), ) MANAGERS = ADMINS DATABASES = { ''default'': { ''ENGINE'': ''django.db.backends.mysql'', # Add ''postgresql_psycopg2'', ''postgresql'', ''mysql'', ''sqlite3'' or ''oracle''. ''NAME'': ''xxxxx'', # Or path to database file if using sqlite3. ''USER'': ''xxxxx'', # Not used with sqlite3. ''PASSWORD'': ''xxxxx'', # Not used with sqlite3. ''HOST'': '''', # Set to empty string for localhost. Not used with sqlite3. ''PORT'': '''', # Set to empty string for default. Not used with sqlite3. } } # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. # On Unix systems, a value of None will cause Django to use the same # timezone as the operating system. # If running in a Windows environment this must be set to the same as your # system time zone. TIME_ZONE = ''America/Chicago'' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = ''en-us'' SITE_ID = 1 # If you set this to False, Django will make some optimizations so as not # to load the internationalization machinery. USE_I18N = True # If you set this to False, Django will not format dates, numbers and # calendars according to the current locale USE_L10N = True # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = '''' # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash if there is a path component (optional in other cases). # Examples: "http://media.lawrence.com", "http://example.com/media/" MEDIA_URL = '''' # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a # trailing slash. # Examples: "http://foo.com/media/", "/media/". ADMIN_MEDIA_PREFIX = ''/media/'' # Make this unique, and don''t share it with anybody. SECRET_KEY = ''#7+qm%hqfe+z8ul5@x_i&sqmu!n=4sa0&i0_#)m99*w$fbk3%#'' # List of callables that know how to import templates from various sources. TEMPLATE_LOADERS = ( ''django.template.loaders.filesystem.Loader'', ''django.template.loaders.app_directories.Loader'', # ''django.template.loaders.eggs.Loader'', ) MIDDLEWARE_CLASSES = ( ''django.middleware.common.CommonMiddleware'', ''django.contrib.sessions.middleware.SessionMiddleware'', ''django.middleware.csrf.CsrfViewMiddleware'', ''django.contrib.auth.middleware.AuthenticationMiddleware'', ''django.contrib.messages.middleware.MessageMiddleware'', ) ROOT_URLCONF = ''mcifdjango.urls'' TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". # Always use forward slashes, even on Windows. # Don''t forget to use absolute paths, not relative paths. ) INSTALLED_APPS = ( ''django.contrib.auth'', ''django.contrib.contenttypes'', ''django.contrib.sessions'', ''django.contrib.sites'', ''django.contrib.messages'', ''django.contrib.admin'', ''django_extensions'', ''mcif'', # Uncomment the next line to enable the admin: # ''django.contrib.admin'', # Uncomment the next line to enable admin documentation: # ''django.contrib.admindocs'', ) TEST_RUNNER = ''mcif.utils.ManagedModelTestRunner'' import os ROOTDIR = os.path.abspath(os.path.dirname(__file__)) TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". # Always use forward slashes, even on Windows. # Don''t forget to use absolute paths, not relative paths. ROOTDIR + ''/mcif/templates'', )

Edición 2:

Aquí está mi clase de Customer ahora:

from django.db import models from django.db import connection, transaction from mcif.models.mcif_model import McifModel class Customer(McifModel): class Meta: db_table = u''customer'' managed = False id = models.BigIntegerField(primary_key=True) customer_number = models.CharField(unique=True, max_length=255) social_security_number = models.CharField(unique=True, max_length=33) name = models.CharField(unique=True, max_length=255) phone = models.CharField(unique=True, max_length=255) deceased = models.IntegerField(unique=True, null=True, blank=True) do_not_mail = models.IntegerField(null=True, blank=True) created_at = models.DateTimeField() updated_at = models.DateTimeField() def distinguishing_column_names(self): return [''name'', ''customer_number'', ''social_security_number'', ''phone'']

Esto es lo que obtengo cuando ejecuto la prueba:

$ ./manage.py test mcif.CustomerUpsertTest Creating test database ''default''... Creating table auth_permission Creating table auth_group_permissions Creating table auth_group Creating table auth_user_user_permissions Creating table auth_user_groups Creating table auth_user Creating table auth_message Creating table django_content_type Creating table django_session Creating table django_site Creating table django_admin_log Installing index for auth.Permission model Installing index for auth.Group_permissions model Installing index for auth.User_user_permissions model Installing index for auth.User_groups model Installing index for auth.Message model Installing index for admin.LogEntry model No fixtures found. E ====================================================================== ERROR: test_upsert (mcif.tests.customer_upsert_test.CustomerUpsertTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/jason/projects/mcifdjango/mcif/tests/customer_upsert_test.py", line 9, in test_upsert customer.upsert() File "/home/jason/projects/mcifdjango/mcif/models/mcif_model.py", line 11, in upsert cursor.execute(self.upsert_sql()) File "/usr/lib/pymodules/python2.6/django/db/backends/mysql/base.py", line 86, in execute return self.cursor.execute(query, args) File "/usr/lib/pymodules/python2.6/MySQLdb/cursors.py", line 166, in execute self.errorhandler(self, exc, value) File "/usr/lib/pymodules/python2.6/MySQLdb/connections.py", line 35, in defaulterrorhandler raise errorclass, errorvalue DatabaseError: (1146, "Table ''test_mcif_django.customer'' doesn''t exist") ---------------------------------------------------------------------- Ran 1 test in 3.724s FAILED (errors=1) Destroying test database ''default''...


Las soluciones presentadas por tarequeh funcionaron para mí después de anular DATABASE_ROUTERS.

Estoy utilizando enrutadores para evitar escrituras en la base de datos heredada. Para solucionar esto, creé un archivo test_settings con el siguiente contenido:

from settings import * DEBUG = True TEST_RUNNER = ''legacy.utils.ManagedModelTestRunner'' DATABASE_ROUTERS = [] DATABASES = { ''default'': { ''ENGINE'': ''django.db.backends.sqlite3'', ''NAME'': os.path.join(HERE, ''test.db''), }, }

Luego, cuando se ejecutan pruebas:

python manage.py test [app_name] --settings=test_settings


No hay suficiente información arriba para responder tu primera pregunta. Sin embargo, una vez que haya resuelto el problema, probablemente querrá instalar django-extensions por el siguiente motivo: tiene un comando sqldiff increíblemente útil que le informará si existe una discrepancia entre la base de datos heredada y su modelo de aplicación.