Testing dynamic apps

90 views
Skip to first unread message

Julien Phalip

unread,
Nov 26, 2008, 6:33:07 PM11/26/08
to Django users
Hi,

I have an app which allows users to rate/vote for any kind of object.
Now, to test this app I need to have a fake model contained in a fake
app. Here's the file structure of my app:

myapp/
tests/
fakeapp/
__init__.py
models.py
__init__.py
models.py
views.py
urls.py

Here is the testing code (located in myapp/tests/__init__.py):

import sys

from django.test import TestCase
from django.conf import settings
from django.core.management import call_command
from django.db.models.loading import load_app

from fakeapp.models import FakeItem

class TestMyApp(TestCase):

def setUp(self):
self.old_INSTALLED_APPS = settings.INSTALLED_APPS
settings.INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'myapp',
'myapp.tests.fakeapp',
)
load_app('myapp.tests.fakeapp')
call_command('syncdb', verbosity=0, interactive=False) #
Create tables for fakeapp

def tearDown(self):
settings.INSTALLED_APPS = self.old_INSTALLED_APPS

def test_blah(self):
item = FakeItem.objects.create(name="blah")
#Do some testing here...

So, all this works pretty well. The 'fakeapp' app is loaded
dynamically, tables are created and the FakeItem model can be use in
my tests.

But it feels dirty. The dynamically created tables can potentially
clash with some other applications (what if someone calls their app
'fakeapp'?). To make it safer maybe I could give a random name to that
app (e.g. 'uytwe876435gjhgdfs0908').

Is there a cleaner way to do this? In a similar app (django-voting
[1]) there is a dedicated test suite, but I'd like to avoid that if
possible.

Thanks a lot for your advice.

Regards,

Julien

[1] http://code.google.com/p/django-voting/source/browse/#svn/trunk/voting/tests

Russell Keith-Magee

unread,
Nov 26, 2008, 6:55:36 PM11/26/08
to django...@googlegroups.com
On Thu, Nov 27, 2008 at 8:33 AM, Julien Phalip <jph...@gmail.com> wrote:
>
> So, all this works pretty well. The 'fakeapp' app is loaded
> dynamically, tables are created and the FakeItem model can be use in
> my tests.
>
> But it feels dirty. The dynamically created tables can potentially
> clash with some other applications (what if someone calls their app
> 'fakeapp'?). To make it safer maybe I could give a random name to that
> app (e.g. 'uytwe876435gjhgdfs0908').
>
> Is there a cleaner way to do this? In a similar app (django-voting
> [1]) there is a dedicated test suite, but I'd like to avoid that if
> possible.

You aren't the first person to have this need: this is logged as
ticket #7835. The problem exists for any project that works at the
meta level, working on models rather than with models. For example
contrib.admin needs test models in order to validate that the admin
can display different types of models correctly.

contrib.admin works around the problem by putting the tests in the
Django system test suite, rather than in the contrib app itself. Based
on your description, this sounds like the same approach Django-voting
has used. This approach works fine if you're willing to maintain an
external test suite, but doesn't provide an integrated test suite that
can be easily distributed with your app.

This is one of those problems that I want to solve, but I haven't yet
given a great deal of though into how to solve. I've got a few vague
ideas banging around in my head about allowing test application and
test model definitions to be included in the test module for an
application, but those ideas aren't fully formed. Anyone that wants to
help out with some concrete suggestions (and even better - code) is
more than welcome to do so.

Yours,
Russ Magee %-)

Julien Phalip

unread,
Nov 26, 2008, 7:07:22 PM11/26/08
to Django users
On Nov 27, 10:55 am, "Russell Keith-Magee" <freakboy3...@gmail.com>
wrote:
Thank you Russell for pointing me to this ticket. I'm quite keen to
find a solution for this, so I will give it some thought and maybe
give a shot at writing some code.

Cheers,

Julien

Rock

unread,
Nov 26, 2008, 8:42:55 PM11/26/08
to Django users
I took a similar path. I cloned the run_tests function from
django.tests.simple.py and put it in my project directory as
test_setup.py while adding this near the top of the function:

if settings.TEST_APPS:
settings.INSTALLED_APPS += settings.TEST_APPS

That at least allows me to define TEST_APPS in my main settings file
for the project and in a location near INSTALLED_APPS so that I am
less likely to introduce unintended collisions. But my approach feels
dirty too. It shouldn't take much work to incorporate a TEST_APPS
construct into the existing test framework. I realize it might be
nicer if each batch of unit tests could define their own set of extra
test models, but I think that a master list of additional test apps
like above has the advantage of simplicity. But I haven't given this a
lot of thought beyond this simple minded hack.

Julien Phalip

unread,
Nov 26, 2008, 9:21:55 PM11/26/08
to Django users
I'm keen to try to find some proper ways to fix this, but before I go
too deep into the bowels of Django's testing framework, I was
wondering what would be the criticisms you'd have about the "solution"
I came up with (at the start of this thread)? It sort of works without
patching Django, but is it acceptable? and if not, why?

At this stage, it looks good enough to me except for two main points
that feel dirty:
1) the test models should be unloaded after the test is run
2) potential conflicts with external apps should be avoided.

Any advice/hint welcome ;)

Julien

Russell Keith-Magee

unread,
Nov 27, 2008, 9:21:49 PM11/27/08
to django...@googlegroups.com
On Thu, Nov 27, 2008 at 11:21 AM, Julien Phalip <jph...@gmail.com> wrote:
>
> I'm keen to try to find some proper ways to fix this, but before I go
> too deep into the bowels of Django's testing framework, I was
> wondering what would be the criticisms you'd have about the "solution"
> I came up with (at the start of this thread)? It sort of works without
> patching Django, but is it acceptable? and if not, why?
>
> At this stage, it looks good enough to me except for two main points
> that feel dirty:
> 1) the test models should be unloaded after the test is run
> 2) potential conflicts with external apps should be avoided.

I will openly admit that I haven't given this much though, except in
the abstract. I'm certain that there are many details and gotchas that
I haven't thought of that implementation will reveal.

Broadly speaking, I'm happy with the approach you are suggesting. The
interface needs some work, though. I'd like to think that at an API
level, it could be as simple as:

class MyMetaTest(TestCase):
apps = ['fakeapp','otherapp']

def test_stuff(self):
...

where 'fakeapp' et al are submodules of the test module that contain a
models.py definition. Obviously, the test applications need to be:
- Added to INSTALLED APPS and the app cache on startup
- Installed in the app cache before the syncdb caused by the pre-test
database flush takes effect. You shouldn't need to manually invoke
syncdb.
- Removed from INSTALLED_APPS and the app cache on teardown

I'm of two minds as to whether apps should define a complete
replacement for INSTALLED_APPS, or if it should be a supplement to the
INSTALLED_APPS that already exists for the project. This hits up
against the recurring issue of testing as a per-app process vs testing
as a project integration process.

The name clash problem you describe shouldn't be that big an issue -
you have the django-admin model validation tools at your disposal. You
will probably want to put in some checks to make sure that validation
only gets called once (so that you're not needlessly revalidating),
but the validator should pick out any name clashes or other conflicts.

Russ %-)

Julien Phalip

unread,
Nov 27, 2008, 9:40:29 PM11/27/08
to Django users
On Nov 28, 1:21 pm, "Russell Keith-Magee" <freakboy3...@gmail.com>
wrote:
Thanks a lot for the feedback and pointers. I'll think about it over
the next few days and will try to post a first sketch patch in the
ticket.
Reply all
Reply to author
Forward
0 new messages