Here at Dimagi, we've been aiming to implement a framework to run browser javascript tests on our Django application. Our Django application is large and thus would require a flexible framework to run the tests. Here are the requirements we had for our framework:
- Ability run tests in the terminal
- Ability run tests in the browser
- Integration with TravisCI
- Simple configuration
- Ability to handle multiple dependencies (i.e. run one test suite with an older version of jQuery and another version with a newer one)
To get started we settled on using mocha
as our test runner and grunt
as our task runner:
npm install mocha
npm install grunt
npm install grunt-mocha
Or just make a package.json
. Check out ours.
To get started, we need to create a test runner HTML file for mocha
to run. There are a couple of strategies to do this, but we decided to take advantage of all the great templating and routing django had to offer by writing a view that rendered an HTML runner. Create a new Django app called mocha
. We'll then need a view, a template, and a route to render a test runner.
The view can be as simple as this:
class MochaView(TemplateView):
def dispatch(self, request, *args, **kwargs):
# Dynamically specifying the app in the template name allows us to reuse code more easily
self.template_name = '{}/spec/mocha.html'.format(kwargs['app'])
return super(MochaView, self).dispatch(request, *args, **kwargs)
Now hook that view up to a route, in CommCareHQ we use /mocha/<app_name>
. Since we will be using the Django templating processor, write the template like any other template you'd write. Ours looks like this. Ensure that you include mocha
and a block for including app dependencies:
<head>
<!-- Mocha and test-only dependencies -->
<link href="{% static 'mocha/mocha.css' %}" type="text/css" rel="stylesheet"></link>
<script src="{% static 'mocha/mocha.js' %}"></script>
<script src="{% static 'chai/chai.js' %}"></script>
<!-- App specific dependencies -->
{% block dependencies %}{% endblock %}
</head>
In the body you'll minimally need to have a block for the mocha tests and start the mocha runner:
<body>
{% block mocha_tests %}{% endblock %}
<script type="text/javascript" charset="utf-8">
// Only tests run in real browser, injected script run if options.run == true
if (navigator.userAgent.indexOf('PhantomJS') < 0) {
mocha.run();
}
</script>
</body>
In order to run the tests from the terminal, and also TravisCI, we'll be using Grunt. You'll need to have grunt-mocha
and point the configuration at the route you just setup. It should look something like this:
grunt.initConfig({
mocha: {
'my_sweet_app': {
options: {
urls: 'http://localhost:8000/mocha/my_sweet_app/',
run: true,
}
}
}
});
grunt.loadNpmTasks('grunt-mocha');
Running grunt mocha
or grunt mocha:my_sweet_app
will run those javascript tests. The last setup needed is to write the template the tests for my_sweet_app
Since we dynamically determine which app to load the template from in the MochaView
, we will write the mocha test template in the my_sweet_app
app.
<!-- mocha.html in my_sweet_app -->
{% extends "mocha/base.html" %}
{% block dependencies %}
<!-- all files that need to be loaded for the tests to run -->
{% endblock %}
{% block mocha_tests %}
<script src="{% static 'my_sweet_app/path/to/spec.js' %}"></script>
{% endblock %}
With that, you should be able to either run your tests in the browser by navigating to /mocha/my_sweet_app
or run the tests in the terminal with grunt my_sweet_app
.
Most of the heavy lifting has been done at this point. At the very least, you'll need to add this to your .travis.yml
:
install:
- python manage.py migrate --noinput
- python manage.py runserver 8000 &
- sleep 5 # wait for server to start
script: 'grunt mocha'
This is probably the quickest and dirtiest way to get this to work. If you want a more robust approach, take a look at our .travis.yml
, specifically these two files: travis.yml
, matrix-runner.sh
.
Above was the simplest way to get started with frontend testing with your Django app. There are quite a few things you can do to spruce up your frontend testing stack. Here are a few things we've done:
- Add in more test dependecies that make javascript testing fun, like
sinon.js
orblanket.js
- Allow for multiple javascript configurations for a single app. This is useful if your Django app has code that runs on multiple pages with differently versioned libraries
- Use TravisCI's matrix capabilities to run Javascript tests independently of your python tests
- Add a
watch
command to yourGruntfile
so your tests automatically run as you save your files