I have been learning about django over the past two weeks or so. I am finally up to the point where I have started learning about its suggested test frameworks. The creators suggest using unittest, which is the standard python unit testing framework. I have liked this aspect of django: even though the core framework was built from ground up, many of its pieces still rely on standard python libraries.

The standard test runner provided by django creates a temporary test database for every test run and deletes it after the tests have completed. I have chosen to use PostgreSQL as my database and creating a new database in there takes longer than creating a simpler in-memory sqlite database. The problem is that django takes the approach of using a single settings file instead of using different ones like Rails’ approach of separate development, production and test settings files. There is a differentiation of setting the DEBUG variable in the settings file, and then relying on that for specifying the different settings. For example…

1
2
3
if DEBUG:
  DATABASE_NAME = "thebitguru_development"
  DATABASE_HOST = ""

But there is no easy way to find out if you are in a test environment. A long time ago (2005!) Ian Maurer suggested modifying DJANGO_SETTINGS_MODULE environment variable, but considering that I only wanted to change one parameter (DATABASE_ENGINE) I thought that was an overkill.

I continued looking around and did not really find any good way of doing this so I was own my own to come with an acceptable way. I read through the command that ran the test at django/db/core/management/test.py, but did not see anything obvious that would help me figure this out in the settings file. After thinking about it for a little while, I finally figured out a way that I could achieve it. Once again, it was python’s “awesomeness” that allowed me to accomplish this. Initially I debated looking back at the stack using traceback, but decided that would also be an overkill. Some more thinking and I ended up with the following code that uses sys.argv.

1
2
3
4
5
6
# Use a different database engine if running tests.
manage_command = filter(lambda x: x.find('manage.py') != -1, sys.argv)
if len(manage_command) != 0:
  command = sys.argv.index(manage_command[0]) + 1
  if command < len(sys.argv) and sys.argv[command] == "test":
    DATABASE_ENGINE = "sqlite3"

The above code is fairly simple and accomplishes the goal. A few days ago I had no idea why lambda functions existed, but now it seems that I am using them all over! You can rewrite the above code to set the TEST variable and then use it again and again, kind of like below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# Determine if we are running in the test environment.
TEST = False
manage_command = filter(lambda x: x.find('manage.py') != -1, sys.argv)
if len(manage_command) != 0:
  command = sys.argv.index(manage_command[0]) + 1
  if command < len(sys.argv):
    TEST = sys.argv[command] == "test"

# . . . some code here . . .

# Use sqlite if running in test
if TEST:
  DATABASE_ENGINE = "sqlite3"

# . . . later in the file . . .

if TEST:
	# set some other variable here

Please note that I am only planning on using sqlite3 when writing my test cases because I don’t want to wait too long. Once I have the tests finalized then I will switch back to PostgreSQL, because as pointed out in a conversation on IRC, you should really test what you are actually going to use!

Back to blog...