2011-09-07 17 views
18

Estoy intentando ejecutar una prueba de unidad con Django 1.3. Normalmente, utilizo MySQL como base de datos, pero como esto es extremadamente lento para una única prueba individual, estoy usando Sqlite3.Tabla faltante al ejecutar Django Unittest con Sqlite3

Así que para cambiar a Sqlite3 sólo para mis unittests, en mi settings.py tengo:

import sys 
if 'test' in sys.argv: 
    DATABASES = { 
     'default': { 
      'ENGINE': 'django.db.backends.sqlite3', 
      'NAME':'/tmp/database.db', 
      'USER'  : '', 
      'PASSWORD' : '', 
      'HOST'  : '', 
     } 
    } 

Cuando ejecuto mi unittest con python manage.py test myapp.Test.test_myfunc, me sale el error:

DatabaseError: no such table: django_content_type 

buscar en Google muestra que hay few de possiblereasons para este error, ninguno de los cuales parece aplicable a mí. No estoy ejecutando Apache, por lo que no veo cómo los permisos serían un problema. El archivo /tmp/database.db se está creando, entonces/tmp es escribible. La aplicación django.contrib.contenttypes está incluida en mis INSTALLED_APPS.

¿Qué me estoy perdiendo?

Editar: Me encontré con este problema de nuevo en Django 1.5, pero ninguna de las soluciones propuestas funciona.

Respuesta

1

Su base de datos probablemente esté vacía, debe configurarse con todas las tablas correspondientes a sus modelos. Normalmente, esto se haría ejecutando python manage.py syncdb primero, para crear todas las tablas de su base de datos. El problema es que en su caso, cuando ejecuta syncdb, python no verá que está ejecutando una prueba, por lo que intentará configurar tablas en su base de datos MySQL.

Para evitar esto, cambiar temporalmente

if 'test' in sys.argv: 

a

if True: 

A continuación, ejecute python manage.py syncdb de configurar las tablas de base de datos SQLite. Ahora que todo está configurado, puede volver a ponerlo en if 'test'... y todo debería funcionar sin problemas. Sin embargo, es probable que desee mover su base de datos fuera del directorio /tmp: django necesita volver a utilizar la misma base de datos cada vez que ejecute sus pruebas, de lo contrario tendrá que crear tablas de base de datos antes de cada prueba.

Tenga en cuenta que si agrega modelos nuevos, deberá repetir este procedimiento para crear las tablas nuevas en sqlite. Si agrega nuevos campos a un modelo existente, necesitará agregar manualmente columnas a su base de datos sqlite usando la interfaz sqlite y ALTER TABLE..., o hacerlo automáticamente usando una herramienta como South.

+0

Dudo que esto va a funcionar. Django destruye la base de datos de prueba después de cada ejecución, así que si estás en lo correcto, necesitaría hacer un syncdb antes de cada ejecución ... – Cerin

+1

FUNCIONA. La base de datos test-Sqlite está [en memoria de forma predeterminada] (https://docs.djangoproject.com/en/1.4/topics/testing/#the-test-database), por lo que no hay archivos para destruir después de una prueba . El archivo que crea con syncdb actúa simplemente como una plantilla para la base de datos de prueba en memoria. Un syncdb solo es necesario cuando la estructura de la base de datos cambia. –

+0

¡Una construcción más ridícula! 'if True' siempre se ejecutará, entonces, ¿por qué perder el tiempo revisándolo? – Bobort

4

Tuve este problema también. Resultó que tuve que agregar una propiedad TEST_NAME en el archivo settings.py para identificar la base de datos de prueba correctamente.Se solucionó el problema para mí:

if 'test' in sys.argv: 
    DATABASES = { 
     'default': { 
      'ENGINE': 'django.db.backends.sqlite3', 
      'NAME': os.path.join(os.path.dirname(__file__), 'test.db'), 
      'TEST_NAME': os.path.join(os.path.dirname(__file__), 'test.db'), 
     } 
    } 
+0

Esto está muy cerca de mi solución real. – Cerin

+0

¡Eso fue muy útil! Si se especifica 'TEST_NAME', Django coloca la base de datos de prueba en un archivo en lugar de mantenerla en la memoria. Por lo tanto, me fue posible suspender el proceso de prueba y abrir el archivo desde otra terminal con 'sqlite3 test.db'. No creo que 'NAME' sea útil para una configuración de prueba; 'TEST_NAME' es probablemente todo lo que necesitamos. – osa

+0

De hecho, concluyo que mis pruebas funcionan con una base de datos en disco pero no funcionan con una base de datos en memoria. – osa

3

Para referencia futura, esto también ocurre si la solicitud no se añade a su INSTALLED_APPS, por ejemplo:

INSTALLED_APPS = (
    ... 
    'myapp' 
) 

De lo contrario se obtiene;

OperationalError: no such table: myapp_mytable 
0

he tenido que añadir las líneas follwoing después de la definición de base de datos de prueba:

from django.core.management import call_command 
call_command('syncdb', migrate=True) 
+0

Esto no funciona para 1.7 (y probablemente no para> = 1.5) – Tom

12

En Django 1.4, 1.5, 1.6, 1.7, o 1.8 se debe ser suficiente para utilizar:

if 'test' in sys.argv: 
    DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3' 

No debería ser necesario anular TEST_NAME , ni llamar al syncdb para ejecutar pruebas. Como señala @osa, el valor predeterminado con el motor SQLite es crear la base de datos de prueba en la memoria (TEST_NAME=':memory:'). No es necesario llamar al syncdb porque el marco de prueba de Django lo hará automáticamente mediante una llamada al syncdb o al migrate según la versión de Django. Puede observar esto con manage.py test -v [2|3].

Muy hablando en términos generales Django configura el entorno de prueba por:

  1. Carga de la base de datos normal NAME de su settings.py
  2. Descubrimiento y construcción de sus clases de prueba (__init__() se llama)
  3. Ajuste de la base de datos NAME al valor de TEST_NAME
  4. Ejecutando las pruebas en contra la base de datos NAME

Aquí está el problema: En el paso 2, NAME todavía está señalando en su base de datos regulares (no de prueba). Si sus pruebas contienen consultas o consultas a nivel de clase en __init__(), se ejecutarán en la base de datos normal, lo que probablemente no sea lo que usted espera. Esto se identifica en bug #21143.

No practico:

class BadFooTests(TestCase): 
    Foo.objects.all().delete()  # <-- class level queries, and 

    def __init__(self): 
     f = Foo.objects.create() # <-- queries in constructor 
     f.save()     #  will run against the production DB 

    def test_foo(self): 
     # assert stuff 

ya que éstas se ejecutan en la base de datos especificada en NAME. Si NAME en esta etapa apunta a una base de datos válida (por ejemplo, su base de datos de producción), la consulta se ejecutará, pero puede tener consecuencias no deseadas.Si ha anulado ENGINE y/o NAME tal que no apunta a una base de datos preexistente, una excepción se produce porque la base de datos de prueba aún no se ha creado:

django.db.utils.DatabaseError: no such table: yourapp_foo # Django 1.4 
DatabaseError: no such table: yourapp_foo     # Django 1.5 
OperationalError: no such table: yourapp_foo    # Django 1.6+ 

En lugar de hacer:

class GoodFooTests(TestCase): 

    def setUp(self): 
     f = Foo.objects.create() # <-- will run against the test DB 
     f.save()     # 

    def test_foo(self): 
     # assert stuff 

Por lo tanto, si ve errores, verifique que sus pruebas no incluyan ninguna consulta que pueda afectar la base de datos fuera de las definiciones de métodos de la clase de prueba.


[1] En Django> = 1,7, DATABASES[alias]['TEST_NAME'] es deprecated a favor de DATABASES[alias]['TEST']['NAME']
[2] Véase el método create_test_db() en db/backends/creation.py

+0

Acaba de actualizar a 1.7, y ahora este error ocurre para 'django.contrib.sites', y la solución que proporciona no funciona. – Cerin

+0

En mi caso, dado que no estábamos usando la función de migraciones incorporadas, solo eliminar la carpeta de migraciones de la aplicación hizo que funcionara –

-1

Para aquellos que intentó todas las formas posibles, pero todavía atrapado en este :

Dado que nuestro código de prueba todavía se está ejecutando en otra máquina, pero no la mía, me trataron de:

  • Crea un nuevo virtual-env. (para aislar el efecto de las aplicaciones)
  • Clonar un nuevo repositorio. (Aislar el efecto de ficheros ignorados)
  • situado en settings utilizar sqlite3 en lugar de psql (aislar el efecto de la base de datos y la configuración de base de datos)
  • Comprobar env variables y .env archivo (que no uso foreman)

Ninguno de esos ayudó. Hasta I:

  1. Cree una cuenta nueva en mi máquina. (Así que comience a limpiar)
  2. Clonar el repositorio.
  3. Ejecutar pruebas -> Correcto
  4. Volver a mi cuenta principal.
  5. Abre una nueva terminal (mata el servidor actual tmux si las estás usando).
  6. pruebas Run -> Éxito

Así que esto no es una verdadera respuesta a su pregunta, ya que no sé lo que estaba mal y cómo se fija, más que otra sugerencia de que usted puede intentar.

4

Sólo para añadir otro caso a esto:

Si usted está tratando de actualizar a 1.8 desde 1.6 (o desde una configuración no migración a una instalación de migración), que puede pegarle a este error si no tiene ejecutar migraciones creadas.

Tuve el mismo problema y tuve que crear migraciones para que el corrector de pruebas pudiera usarlas, lo que no era intuitivo porque las migraciones previas, las pruebas simplemente crearían un nuevo DB basado en hacer syncdb, que siempre funcionó.

2

Habiendo probado todo lo anterior que finalmente descubrí otra razón de que esto puede ocurrir: -

Si alguno de sus modelos no son creados por uno de sus migraciones.

he hecho un poco de depuración y parece que las pruebas de Django configura la base de datos mediante la aplicación de todas las migraciones en orden, empezando por 001_initial.py, antes de tratar de seleccionar de entre las tablas en función de su models.py

En mi caso, una tabla no se había agregado de alguna manera a las migraciones, pero se agregó manualmente, por lo que no se pudo aplicar correctamente el conjunto de migración completo. Cuando corregí manualmente la migración 001_initial.py para crear esta tabla, el OperationalError desapareció.

-1

Para cualquiera que llegue aquí, buscando por qué Django sigue creando una base de datos independientemente de la opción --keepdb. Por qué dice Using existing test database for alias 'default', pero luego ejecuta un montón de instrucciones CREATE TABLE.

Si no establece una configuración DATABASES > default > TEST > NAME, Django intentará usar en la base de datos de la memoria, y no se mantendrá, por lo tanto, configúrelo y anule los valores predeterminados.

Puede que sea así:

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(ROOT_DIR, 'data', 'db.dev.sqlite3'), 'TEST': { 'NAME': os.path.join(ROOT_DIR, 'data', 'db.test.sqlite3'), } } }

Cuestiones relacionadas