Skip to content

Instantly share code, notes, and snippets.

@benrudolph
Last active November 19, 2015 05:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benrudolph/efa167346dbceb6d9ad0 to your computer and use it in GitHub Desktop.
Save benrudolph/efa167346dbceb6d9ad0 to your computer and use it in GitHub Desktop.

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:

  1. Ability run tests in the terminal
  2. Ability run tests in the browser
  3. Integration with TravisCI
  4. Simple configuration
  5. Ability to handle multiple dependencies (i.e. run one test suite with an older version of jQuery and another version with a newer one)

Getting started

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.

Configuring the mocha Django app

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>

Configuring Grunt

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

The Django app tests

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.

TravisCI integration

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.

Bonuses

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 or blanket.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 your Gruntfile so your tests automatically run as you save your files
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment