Still stuck trying to set up testing

58 views
Skip to first unread message

Todd O'Bryan

unread,
Jun 8, 2006, 11:07:51 PM6/8/06
to django...@googlegroups.com
I'm having a chicken-and-egg problem.

So, I want to drop the test database, recreate it, sync it to the
models, and then populate it with test data, ideally automatically
because automatic tests are far more likely to get run.

If I want the drop and create commands to be generic, they need
access to the current DATABASE_NAME, so they have to be called while
the server or shell is running so that they pick up any --settings=
options from the command line and know which database to use.

But then I have to run syncdb, which I can't figure out how to run
under program control. Well, I can, but it prints out all the stuff
it would print if it was called on the command line and stops to ask
if I want to create a superuser, which is kind of annoying in the
middle of a test script.

And then I need to be running under program control again to add all
my test data to the the db.

Please tell me that I'm overthinking this and there's some easy way
out of this!

Thanks,
Todd

Malcolm Tredinnick

unread,
Jun 8, 2006, 11:18:29 PM6/8/06
to django...@googlegroups.com

Warning: I haven't tested what I'm about to suggest here, but it should
come close...

It sounds like you want to do the equivalent of "manage.py reset" --
drop all the data and tables and recreate them for a bunch of apps that
you know about. So have a look at how django/core/management.py
implements the reset() method. Duplicating the three or four lines of
code that do the real work there should not be too hard. Looping through
each of your apps, you basically want to do

sql_list = get_sql_reset(app)
cursor = connection.cursor()
for sql in sql_list:
cursor.execute(sql)

with some exception handling wrapped in somewhere. Doing that inside
your test framework will work, won't it. And you're not duplicating too
much code here: the above fragement uses core.management.get_sql_reset()
to do all the heavy lifting.

Best wishes,
Malcolm


Michael Radziej

unread,
Jun 9, 2006, 4:47:58 AM6/9/06
to django...@googlegroups.com

Here's one hackish version for db initialization, which is still work in progress and you'll want to adopt it to your needs:

def createdb():
from StringIO import StringIO
from django.contrib.auth.models import User

if settings.DATABASE_ENGINE == "sqlite3":
settings.DATABASE_NAME = ":memory:"
else:
cursor = db.connection.cursor()
try:
db.connection.connection.autocommit(1)
except:
pass
try:
cursor.execute("CREATE DATABASE %s" % settings.TEST_DATABASE_NAME)
except Exception, e:

sys.stderr.write("Got an error creating the test database: %s\n" % e)
if settings.TEST_DATABASE_NAME.startswith('test_'):
confirm = "yes"
else:
confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % settings.TEST_DATABASE_NAME)
if confirm == 'yes':
cursor.execute("DROP DATABASE %s" % settings.TEST_DATABASE_NAME)
cursor.execute("CREATE DATABASE %s" % settings.TEST_DATABASE_NAME)
else:
abort_test()
db.connection.close()
settings.DATABASE_NAME = settings.TEST_DATABASE_NAME

fake_in=StringIO("no\n")
stdin = sys.stdin
sys.stdin = fake_in
syncdb()
sys.stdin = stdin
admin=User(username="...", is_staff=True, is_superuser=True)
admin.set_password("...")
admin.save()


Another version that avoids that hackish io redirection works like this, from a posting of mamcxyz:

# disable the "create a super user" question
dispatcher.disconnect(create_superuser, sender=auth_app,
signal=signals.post_syncdb)
management.syncdb()

Michael

mamcxyz

unread,
Jun 9, 2006, 12:14:37 PM6/9/06
to Django users
I do this and work terrible well. I can debug it from Komodo, I can set
breakpoints, Is FAST (all test is under memory)

#Clase sobre la cual basar los test...
import unittest
import sys
import os

SETTINGS_MODULE = 'settings'

project_dir = os.path.abspath('../')
sys.path.append(os.path.join(project_dir, '..'))
sys.path.append("..")
sys.path.pop()
os.environ['DJANGO_SETTINGS_MODULE'] = 'jhonWeb.settings'

from django.conf import settings
from django.core import management
from django import db
from django.contrib.auth.management import create_superuser
from django.contrib.auth import models as auth_app
from django.db.models import signals
from django.db import connection
from django.dispatch import dispatcher


from jhonWeb.core.models import Country,City,State,Settings
from jhonWeb.blogs.models import Blog
from jhonWeb.links.models import Link
from jhonWeb.multimedia.models import *
from jhonWeb.restaurant.models import Restaurant,Bono,RestaurantAddress
from jhonWeb.core.install import InstallJhonWeb

class BaseTestJhonWeb(unittest.TestCase):
def _testSetup(self):
pass

def setUp(self):
# Change Settings
settings.DATABASE_NAME = ':memory:'
settings.DATABASE_ENGINE = 'sqlite3'

# Install Models

cursor = connection.cursor()
self._set_autocommit(connection)

# disable the "create a super user" question

try:
dispatcher.disconnect(create_superuser, sender=auth_app,
signal=signals.post_syncdb)
except:
pass

management.syncdb()

# Reload DB module
#reload(db)

self._testSetup()

def _set_autocommit(self, connection):
"""
Make sure a connection is in autocommit mode.
"""
if hasattr(connection.connection, "autocommit"):
connection.connection.autocommit(True)
elif hasattr(connection.connection, "set_isolation_level"):
connection.connection.set_isolation_level(0)

class BaseTestWithTestData(BaseTestJhonWeb):
def _testSetup(self):
if hasattr(self,'DataInstalled')==False:
# NOTE: This is my custom setup for my application, ignore it if want
oSetup=InstallJhonWeb(crearDatos=False)
oSetup.EraseAllData()
oSetup.RunInstall()


Blog(tags='Categoria2\Producto1',userId='b1',title='Blog1',content='*Contenido
Blog*').save()

Blog(tags='Categoria1\Producto1,Categoria2\Producto2',userId='b2',title='Blog2',content='h2.Contenido
Blog').save()

Blog(tags='Producto3',userId='b3',title='Blog3',content='-Contenido-
Blog').save()

Blog(tags='Producto4,Categoria1\Producto5',userId='b4',title='Blog4',content='#Contenido
Blog').save()

self.DataInstalled = True

self.assertEqual(4,Blog.objects.count())

In a specific test:

#Probar las clases de restaurantes
import unittest
from BaseTest import BaseTestWithTestData
from jhonWeb.core.models import City

class testRestaurant(BaseTestWithTestData):
def testBasic(self):
lista = Blogs.objects.filter(userId='r1')

self.assertEqual(lista.count(),1)

restaurante = lista[0]

,... more stuff here

Neilen Marais

unread,
Jul 2, 2006, 8:30:14 PM7/2/06
to django...@googlegroups.com
Hi

On Thu, 08 Jun 2006 23:07:51 -0400, Todd O'Bryan wrote:


> And then I need to be running under program control again to add all
> my test data to the the db.
>
> Please tell me that I'm overthinking this and there's some easy way
> out of this!

The other posters in this thread do give nice solutions. I just
extracted the minimum needed to create a testing DB in memory using
sqlite. This is what I have:

----- cut here -----
import os
os.environ['DJANGO_SETTINGS_MODULE']='MYSITE.settings'

def createdb():
from django import db
from django.conf import settings
settings.DATABASE_ENGINE = "sqlite3"
settings.DATABASE_NAME = ":memory:"
reload(db)
from django.core.management import syncdb
from django.dispatch import dispatcher
from django.dispatch.errors import DispatcherKeyError


from django.contrib.auth.management import create_superuser
from django.contrib.auth import models as auth_app
from django.db.models import signals

db.connection.close()


# disable the "create a super user" question
try:
dispatcher.disconnect(create_superuser, sender=auth_app,
signal=signals.post_syncdb)

except DispatcherKeyError:
# This must be run only once per session
pass
syncdb()
----- cut here -----

This presumes that MYSITE is in sys.path. I find that using nosetest
(http://somethingaboutorange.com/mrl/projects/nose/) from the
directory below MYSITE works pretty well, since it takes care of
sys.path.

Another comment: The placement of from django import db, and
reload(db) seems to be critical. The django.db module needs to be
imported before django.settings, and has to be reloaded after changing
settings.DATABASE_ENGINE. If you were already using sqlite I guess
that's not a problem.

>
> Thanks,

--
you know its kind of tragic
we live in the new world
but we've lost the magic
-- Battery 9 (www.battery9.co.za)

Reply all
Reply to author
Forward
0 new messages