tutorial - pruebas unitarias django
¿Elegir la base de datos de prueba? (10)
Agregué esto a los comentarios anteriores, pero se perdió un poco: los cambios recientes en la webacción hacen que esto sea MUCHO más fácil. Ahora puedes crear here .
Siga las instrucciones allí y, al crear un nuevo usuario, asegúrese de darles el permiso para ALTER USER new_username CREATEDB;
.
Es probable que también deba cambiar la configuración cron predeterminada para que no intenten verificar si esta base de datos está activa y en funcionamiento con tanta frecuencia.
Estoy tratando de correr
./manage.py test
Pero me dice
Se produjo un error al crear la base de datos de prueba: permiso denegado para crear la base de datos
Obviamente, no tiene permiso para crear la base de datos, pero estoy en un servidor compartido, así que no hay mucho que pueda hacer al respecto. Puedo crear una nueva base de datos a través del panel de control, pero no creo que haya ninguna manera de permitir que Django lo haga automáticamente.
Entonces, ¿no puedo crear la base de datos de prueba manualmente y en lugar de decirle a Django que la vacíe cada vez, en lugar de recrear todo?
Creo que una mejor solución podría ser definir su propio corredor de prueba .
El siguiente es un corredor de la suite de pruebas de django para crear una base de datos utilizando la API Webfaction XML-RPC . Tenga en cuenta que la configuración de la base de datos mediante la API puede tardar hasta un minuto, y el script puede parecer que se ha quedado atascado momentáneamente, solo espere un poco.
NOTA: existe un riesgo de seguridad de tener una contraseña del panel de control en el servidor de webfaction, ya que alguien que incurra en el SSH de su servidor web podría hacerse cargo de su cuenta. Si eso es una preocupación, establezca USE_SESSKEY en Verdadero y use el script de estructura debajo de este script para pasar una identificación de sesión al servidor. La clave de sesión caduca en 1 hora desde la última llamada a la API.
Archivo test_runner.py: en el servidor, debe configurar ./manage.py test para usar WebfactionTestRunner
"""
This test runner uses Webfaction XML-RPC API to create and destroy database
"""
# you can put your control panel username and password here.
# NOTE: there is a security risk of having control panel password in
# the webfaction server, because someone breaching into your web server
# SSH could take over your Webfaction account. If that is a concern,
# set USE_SESSKEY to True and use the fabric script below this script to
# generate a session.
USE_SESSKEY = True
# CP_USERNAME = ''webfactionusername'' # required if and only if USE_SESSKEY is False
# CP_PASSWORD = ''webfactionpassword'' # required if and only if USE_SESSKEY is False
import sys
import os
from django.test.simple import DjangoTestSuiteRunner
from django import db
from webfaction import Webfaction
def get_sesskey():
f = os.path.expanduser("~/sesskey")
sesskey = open(f).read().strip()
os.remove(f)
return sesskey
if USE_SESSKEY:
wf = Webfaction(get_sesskey())
else:
wf = Webfaction()
wf.login(CP_USERNAME, CP_PASSWORD)
def get_db_user_and_type(connection):
db_types = {
''django.db.backends.postgresql_psycopg2'': ''postgresql'',
''django.db.backends.mysql'': ''mysql'',
}
return (
connection.settings_dict[''USER''],
db_types[connection.settings_dict[''ENGINE'']],
)
def _create_test_db(self, verbosity, autoclobber):
"""
Internal implementation - creates the test db tables.
"""
test_database_name = self._get_test_db_name()
db_user, db_type = get_db_user_and_type(self.connection)
try:
wf.create_db(db_user, test_database_name, db_type)
except Exception as e:
sys.stderr.write(
"Got an error creating the test database: %s/n" % e)
if not autoclobber:
confirm = raw_input(
"Type ''yes'' if you would like to try deleting the test "
"database ''%s'', or ''no'' to cancel: " % test_database_name)
if autoclobber or confirm == ''yes'':
try:
if verbosity >= 1:
print("Destroying old test database ''%s''..."
% self.connection.alias)
wf.delete_db(test_database_name, db_type)
wf.create_db(db_user, test_database_name, db_type)
except Exception as e:
sys.stderr.write(
"Got an error recreating the test database: %s/n" % e)
sys.exit(2)
else:
print("Tests cancelled.")
sys.exit(1)
db.close_connection()
return test_database_name
def _destroy_test_db(self, test_database_name, verbosity):
"""
Internal implementation - remove the test db tables.
"""
db_user, db_type = get_db_user_and_type(self.connection)
wf.delete_db(test_database_name, db_type)
self.connection.close()
class WebfactionTestRunner(DjangoTestSuiteRunner):
def __init__(self, *args, **kwargs):
# Monkey patch BaseDatabaseCreation with our own version
from django.db.backends.creation import BaseDatabaseCreation
BaseDatabaseCreation._create_test_db = _create_test_db
BaseDatabaseCreation._destroy_test_db = _destroy_test_db
return super(WebfactionTestRunner, self).__init__(*args, **kwargs)
Archivo webfaction.py: este es un contenedor delgado para la API de Webfaction, debe ser importable tanto por test_runner.py (en el servidor remoto) como por fabfile.py (en la máquina local)
import xmlrpclib
class Webfaction(object):
def __init__(self, sesskey=None):
self.connection = xmlrpclib.ServerProxy("https://api.webfaction.com/")
self.sesskey = sesskey
def login(self, username, password):
self.sesskey, _ = self.connection.login(username, password)
def create_db(self, db_user, db_name, db_type):
""" Create a database owned by db_user """
self.connection.create_db(self.sesskey, db_name, db_type, ''unused'')
# deletes the default user created by Webfaction API
self.connection.make_user_owner_of_db(self.sesskey, db_user, db_name, db_type)
self.connection.delete_db_user(self.sesskey, db_name, db_type)
def delete_db(self, db_name, db_type):
try:
self.connection.delete_db_user(self.sesskey, db_name, db_type)
except xmlrpclib.Fault as e:
print ''ignored error:'', e
try:
self.connection.delete_db(self.sesskey, db_name, db_type)
except xmlrpclib.Fault as e:
print ''ignored error:'', e
Archivo fabfile.py: una secuencia de comandos de estructura de muestra para generar la clave de sesión, necesaria solo si USE_SESSKEY = True
from fabric.api import *
from fabric.operations import run, put
from webfaction import Webfaction
import io
env.hosts = ["[email protected]"]
env.password = "webfactionpassword"
def run_test():
wf = Webfaction()
wf.login(env.hosts[0].split(''@'')[0], env.password)
sesskey_file = ''~/sesskey''
sesskey = wf.sesskey
try:
put(io.StringIO(unicode(sesskey)), sesskey_file, mode=''0600'')
# put your test code here
# e.g. run(''DJANGO_SETTINGS_MODULE=settings /path/to/virtualenv/python /path/to/manage.py test --testrunner=test_runner.WebfactionTestRunner'')
raise Exception(''write your test here'')
finally:
run("rm -f %s" % sesskey_file)
La respuesta aceptada no funcionó para mí. Está tan desactualizado, que no se ejecutó en mi base de código heredada con djano 1.5.
Escribí una entrada de blog que describía por completo cómo resolví este problema creando un corredor de prueba alternativo y cambiando la configuración de django para proporcionar toda la configuración necesaria y usar un nuevo corredor de prueba.
Mi variante para reutilizar la base de datos:
from django.test.simple import DjangoTestSuiteRunner
from django.core.management import call_command
class TestRunner(DjangoTestSuiteRunner):
def setup_databases(self, **kwargs):
from django.db import connections
settings = connections[''default''].settings_dict
settings[''NAME''] = settings[''TEST_NAME'']
settings[''USER''] = settings[''TEST_USER'']
settings[''PASSWORD''] = settings[''TEST_PASSWD'']
call_command(''syncdb'', verbosity=1, interactive=False, load_initial_data=False)
def teardown_databases(self, old_config, **kwargs):
from django.db import connection
cursor = connection.cursor()
cursor.execute(''show tables;'')
parts = (''DROP TABLE IF EXISTS %s;'' % table for (table,) in cursor.fetchall())
sql = ''SET FOREIGN_KEY_CHECKS = 0;/n'' + ''/n''.join(parts) + ''SET FOREIGN_KEY_CHECKS = 1;/n''
connection.cursor().execute(sql)
Modifique los siguientes métodos en django/db/backends/creation.py
:
def _destroy_test_db(self, test_database_name, verbosity):
"Internal implementation - remove the test db tables."
# Remove the test database to clean up after
# ourselves. Connect to the previous database (not the test database)
# to do so, because it''s not allowed to delete a database while being
# connected to it.
self._set_test_dict()
cursor = self.connection.cursor()
self.set_autocommit()
time.sleep(1) # To avoid "database is being accessed by other users" errors.
cursor.execute("""SELECT table_name FROM information_schema.tables WHERE table_schema=''public''""")
rows = cursor.fetchall()
for row in rows:
try:
print "Dropping table ''%s''" % row[0]
cursor.execute(''drop table %s cascade '' % row[0])
except:
print "Couldn''t drop ''%s''" % row[0]
#cursor.execute("DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name))
self.connection.close()
def _create_test_db(self, verbosity, autoclobber):
"Internal implementation - creates the test db tables."
suffix = self.sql_table_creation_suffix()
if self.connection.settings_dict[''TEST_NAME'']:
test_database_name = self.connection.settings_dict[''TEST_NAME'']
else:
test_database_name = TEST_DATABASE_PREFIX + self.connection.settings_dict[''NAME'']
qn = self.connection.ops.quote_name
# Create the test database and connect to it. We need to autocommit
# if the database supports it because PostgreSQL doesn''t allow
# CREATE/DROP DATABASE statements within transactions.
self._set_test_dict()
cursor = self.connection.cursor()
self.set_autocommit()
return test_database_name
def _set_test_dict(self):
if "TEST_NAME" in self.connection.settings_dict:
self.connection.settings_dict["NAME"] = self.connection.settings_dict["TEST_NAME"]
if "TEST_USER" in self.connection.settings_dict:
self.connection.settings_dict[''USER''] = self.connection.settings_dict["TEST_USER"]
if "TEST_PASSWORD" in self.connection.settings_dict:
self.connection.settings_dict[''PASSWORD''] = self.connection.settings_dict["TEST_PASSWORD"]
Parece funcionar ... solo agregue la configuración adicional a su settings.py
si la necesita.
Podría usar django-nose como su TEST_RUNNER. Una vez instalado, si pasa la siguiente variable de entorno, no eliminará ni volverá a crear la base de datos (créela manualmente primero).
REUSE_DB=1 ./manage.py test
También puedes agregar lo siguiente a settings.py para que no tengas que escribir REUSE_DB = 1 cada vez que quieras ejecutar pruebas:
os.environ[''REUSE_DB''] = "1"
Nota: esto también dejará todas sus tablas en las bases de datos, lo que significa que la configuración de la prueba será un poco más rápida, pero tendrá que actualizar las tablas manualmente (o eliminar y volver a crear la base de datos usted mismo) cuando cambie sus modelos.
Tuve un problema similar Pero quería que Django simplemente omitiera la creación de una base de datos de prueba para una de mis instancias (no es un espejo rudo). Siguiendo la sugerencia de Mark, creé un corredor de prueba personalizado, como sigue
from django.test.simple import DjangoTestSuiteRunner
class ByPassableDBDjangoTestSuiteRunner(DjangoTestSuiteRunner):
def setup_databases(self, **kwargs):
from django.db import connections
old_names = []
mirrors = []
for alias in connections:
connection = connections[alias]
# If the database is a test mirror, redirect its connection
# instead of creating a test database.
if connection.settings_dict[''TEST_MIRROR'']:
mirrors.append((alias, connection))
mirror_alias = connection.settings_dict[''TEST_MIRROR'']
connections._connections[alias] = connections[mirror_alias]
elif connection.settings_dict.get(''BYPASS_CREATION'',''no'') == ''no'':
old_names.append((connection, connection.settings_dict[''NAME'']))
connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive)
return old_names, mirrors
Luego creé una entrada de dictado adicional en una de mis entradas de bases de datos dentro de settings.py, ''BYPASS_CREATION'':''yes'',
Finalmente, configuré un nuevo TestRunner con
TEST_RUNNER = ''auth.data.runner.ByPassableDBDjangoTestSuiteRunner''
Yo sugeriría usar sqlite3 para propósitos de prueba mientras se sigue usando mysql / postgres / etc para la producción.
Esto se puede lograr colocando esto en su archivo de configuración:
if ''test'' in sys.argv:
DATABASES[''default''] = {''ENGINE'': ''django.db.backends.sqlite3''}
Ver Ejecución de pruebas de django con sqlite.
se creará un archivo de base de datos temporal sqlite en la página de inicio de su proyecto django, al que tendrá acceso de escritura. La otra ventaja es que sqlite3 es mucho más rápido para las pruebas. Sin embargo, puede tener problemas si está utilizando algún sql sin formato específico de mysql / postgres (que debería intentar evitar de todos modos).
TEST_DATABASE_PREFIX
simple : cambie TEST_DATABASE_PREFIX
en django/db/backends/base/creation.py
como desee.