Programming: A concise tutorial on django-nose-selenium.

So you’d really like to perform integration testing on your Django site, to exercise the whole stack right from the client-side Javascript through to the database at the backend. There are a million ways to do this, but only a few work. Of the few that work, none have a fully worked out example showing you how to get them up and running. Enter this blog post! In this, I’m going to show you as compactly as possible how to get django-nose-selenium working on your Django 1.3 test setup.

First install the necessary packages. I use virtualenv (you should do), so that means:
[shell]
pip install django-nose
pip install git+https://github.com/weluse/django-nose-selenium.git
# we’re going to use the cherrypyliveserver
pip install CherryPy
[/shell]
Better yet, add these to your pip requirements file.

In your settings.py file, make the following modifications and additions:
[python]
# so that "python manage.py test" invokes django-nose-selenium
TEST_RUNNER = ‘django_nose.NoseTestSuiteRunner’
# should match SELENIUM_URL_ROOT which defaults to http://127.0.0.1:8000
LIVE_SERVER_PORT = 8000
.
.
DATABASES = {
‘default’: {
# TEST_NAME is absolutely CRITICAL for getting django-nose-selenium
# going with sqlite3. The default in-memory breaks everything.
‘TEST_NAME’: os.path.join(project_path, ‘test_sqlite.db’),
.
.
}
}
.
.
INSTALLED_APPS = (
.
.
# this HAS to go after ‘south’ if you have that in your INSTALLED_APPS
‘django_nose’
)
[/python]

Most importantly here, if you’re using sqlite3, is to specify TEST_NAME. If you don’t do this, Django by default uses an in memory sqlite3 database, and this breaks in infuriatingly mysterious ways, for example yielding “DatabaseError: no such table: django_site” or the same for “django_session”, while you can see that the test framework is creating those tables. After hours of frustration, I stumbled upon this important tidbit in this django-nose-selenium issue report.

Now you can start writing tests! Below is an example, which lives in project/app/tests.py, that loads up the front page, and also tries to log in. You can use the selenium IDE (firefox extension) to generate the Python exercise code you see below. I’m also making use of selenium_fixtures to load in two users into the database (the fixture itself is shown below this example):

[python]
from django.utils import unittest
from noseselenium.cases import SeleniumTestCaseMixin
from the_project.the_app.models import User

class TestSelenium(unittest.TestCase, SeleniumTestCaseMixin):
# this fixture adds two users: admin/admin and test/test
# this data stays in the specially created test DB for the whole run!
selenium_fixtures = [‘test_data1.json’]

def test_ok(self):
""" check that the front page has correctly loaded
and that there’s a login link.
"""
sel = self.selenium
sel.open("/")
self.failUnless(sel.is_text_present("Login to System"))

def test_login(self):
"""Test that the user can actually login."""

sel = self.selenium
sel.open("/")

sel.click("link=Login")
sel.wait_for_page_to_load("30000")
sel.type("id=username", "test")
sel.type("id=password", "test")
sel.click("css=input[type=\"submit\"]")
sel.wait_for_page_to_load("30000")

self.failUnless(sel.is_text_present("Logout (test)"))

[/python]

I generated the fixture with the django dumpdata command. It exists as project/app/fixtures/test_data1.json and it looks as follows (one of the two users removed for brevity):
[js]
[
{
"pk": 1,
"model": "auth.user",
"fields": {
"username": "admin",
"first_name": "",
"last_name": "",
"is_active": true,
"is_superuser": true,
"is_staff": true,
"last_login": "2011-08-27 01:02:53",
"groups": [],
"user_permissions": [],
"password": "sha1$0faea$31f62ca99c405cad9cbe0bd469baeb8b3b6b923c",
"email": "admin@admin.com",
"date_joined": "2011-08-27 01:02:37"
}
},
[/js]

Once all of this has been setup, first startup the Selenium RC server as explained here. In short, download from here, and invoke like this:
[shell]
java -jar selenium-server-standalone-2.5.0.jar
[/shell]

Then, finally, invoke the tests as follows:
[shell]
python manage.py test –with-selenium –with-cherrypyliveserver –with-selenium-fixtures
[/shell]

–with-selenium specifies that selenium tests should be executed, –with-selenium-fixtures that selenium fixtures should be loaded and cherrypyliveserver that the CherryPy multi-threaded webserver should be used instead of the built-in django one that’s not really multi-threading ready.

Let me know in the comments if this helped, or if I need to add or fix anything.

P.S. I also wasted a few precious hours with django-sane-testing. Fixtures didn’t work, python manage.py test didn’t work in spite of having correct TEST_RUNNER configured, and it also didn’t serve static files. It also complained about django_session not existing, so it could be that specifying TEST_NAME there might help for that bit.

Programming: Fix for cancel button weirdness with jQuery Jeditable onblur=submit

Jeditable is a great jQuery plugin for when you want to do those neat HTML text fields that turn into input boxes when you click on them, almost like the fields in the GMail contact editor. Whilst working on our all-singing and dancing super interactive web-based time-management tool, we ran into a bug with tho onblur=submit mode of Jeditable. In this mode, clicking anywhere outside of the dynamic input box submits the text. However, clicking on the cancel button would cause a full page reload, something that is less than desirable for highly-interactive single-page HTML apps.

We fixed this bug with a smal patch that you can download here. The fix very simply sets an attribute on the form when the user presses the cancel button. In the blur event handler it checks for this variable and does NOT submit if it’s set.

I also posted a comment concerning this on the Jeditable website.

If this by any chance helps you, pipe up in the comments! We’ll be posting more of these little tidbits in the future.