JS TDD in your Django apps

Yesterday, I confessed my guilt of writing shitty DOM manipulating JS code live in the browser, and said no, never again.

Due to positive feedback in the django-users mailing list, I’m writing how I actually plan to change how I do JS in Django apps. Believe me, it’s preventively fixing issues i’ve had with JS in Django apps during the last ten years. Nuff said, let’s hack !

Let’s create an index.test.js file:

/* global describe, jest, test, expect */
import * as yourmodule from './index'

describe('Awesome', () => {
  test('ctor()', () => {
    let x = new yourmodule.Awesome(2)

And an index.js file with our runtime code:

class Awesome {
  constructor(something) {
    this.something = something

export {

You need to install nodejs, because we’ll run unit tests on it. You also need npm or yarn because we’ll install JS modules with it - yay, same development workflows with JS as we have in Python, get ready for the XXIst century ! You could do the article in pure npm, but we’ll use yarn:

sudo npm install -g yarn

In your django app source code, run:

yarn init

This creates a package.json file, which is the equivalent of your setup.py, but for JS packages. We want more JS features, for this we’ll use npm packages:

  • Babel for JS transpiling,
  • jest for executing tests,
  • webpack for assembling our JS,
  • eslint for analysing our JS,

We’ll install them with the yarn add command:

yarn add babel-polyfill babel-core babel-jest babel-loader babel-preset-env babel-preset-stage-2 eslint jest webpack@3

This will create a new directory node_modules, which is like the virtualenv for python, install jest and dependencies in it, and it will also create a file which you need to add to git: yarn.lock, the equivalent of requirements.txt.

Next, create a .babelrc file to load the env preset:

  "presets": ["env"]

We can now run the test runner with command:


Now, let’s build a webpack entrypoint, add a webpack.config.js to create a jstdd.js file in static/ directory based on index.js source ode, so django will be able to resolve it with staticfiles, as such:

var path = require('path')

module.exports = {
  entry: {
    main: ['babel-polyfill', './index.js'],
  output: {
    path: path.resolve('static'),
    filename: 'jstdd.js'
  devtool: 'source-map',
  module: {
    rules: [
        test: /\.js$/,
        //exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['stage-2', 'babel-preset-env']

Now, you can build the static/jstdd.js file ready for production:


Next, open your package.json, we’ll add a scripts configuration:

We’ll add a script section as such:

  "name": "jstdd",
  "scripts": {
    "start": "webpack --watch",
    "prepare": "webpack",
    "test": "jest",
    "lint": "eslint ."

When a command is in scripts, it does not need to be specified with full ./node_modules path, we can now run those commands:

yarn test # runs js tests yarn test – –watch # watch js tests yarn run lint # runs eslint and hurt your feelings yarn prepare # build your JS for serving with webpack yarn start # start webpack watcher yarn publish # publish your package on npm

For a more complete example, with coverage, pipeline and all, see facond

Have FUN !