Rover.com is a Django shop, but I personally come from a Rails background. The Rails world has great tooling and infrastructure for automated functional tests – capybara, capybara-webkit, and the new hotness poltergist. Underneath poltergeist lies PhantomJS, a headless webkit with very few dependencies, excellent for automated testing. Unfortuantely, PhantomJS version 1.5 dropped Python bindings, leaving us Djangonauts out to dry. There also isn’t a great capybara equivalent in the Python world (Ghost.py is the closest).
Thankfully, despite the roadblocks, there is a path forward! Django (starting with 1.4) comes with
LiveServerTestCase to support our exact use case. After failed attempts to get Ghost.py up and running (due to dependencies and lack of documentation), I landed upon a solution that will start us Django developers down the path of being first class citizens once more (well, maybe not exactly first class, but at least those coach seats up front with a little extra legroom). I’ve also taken some baby steps to improve our testing assertion syntax, trying to fill the capybara void, which I’ll get into at the end of this post.
Let’s get started.
Install the Prerequisites
The following steps are for Ubuntu 12.04 LTS.
Ubuntu 12.04’s default ppa’s don’t have the latest version of NodeJS, so we first need to add a new repository.
1 2 3 4 5
Now, we can install
For use with PhantomJS
NodeJS comes with npm, their package manager. From that, we can install PhantomJS.
Note here that I’m installing it globally, which is not required to get this to work, but can make things easier.
With our system set up to support our automated testing, we now need to set up our application so we can start writing our own tests.
Add selenium to your
1 2 3 4 5
Install the Selenium Utility Belt
We’ve written and open-sourced the very beginning of what we hope will grow into a more fully-featured assertion framework Selenium testing in Django. We created this to wrap some of the selenium syntax into a more expressive feature set to enable more rapid test writing. When testing interfaces, it is really nice to be able to express assertions as they relate to your goals, such as
assertOnPage. This was the impetus for creating the selenium utility belt. The project lives at https://github.com/roverdotcom/selenium-utility-belt and is in its infancy. We hope to add to this (with your help!), rework its structure, and release it onto PyPI for wider consumption.
selenium_utility_belt.py file into your project at a convenient place to import it to your base test case class. We toss ours in our common app, in the test folder.
Base class for your test cases
1 2 3 4 5 6 7
Write your first test!
With just a little bit of wrapping of the Selenium API, our interface becomes very simple to test. Here we have a single test that asserts the presence and visibility of our Location text entry field on our homepage.
1 2 3 4 5 6 7 8 9 10
When running these tests on our CI server, we ran into the issue of port collisions for the running PhantomJS processes.
LiveServerTestCase had foresight on this issue, and added an option when you run your tests. Simply specify the range of ports to use when you call your test runner.
Recent versions of django-jenkins take this option as well, so you can easily use these tests on your CI server
If you have run into any questions or hit any roadblocks along your way here, you can reach me at @croby. Contributions to the utility belt are welcome and encouraged!