JavaScript Test-Driven Development with Rails 4 & Konacha

JavaScript Test-Driven Development with Rails 4 & Konacha

At Contactually, our app’s back-end is written in Rails 4, while most of the front-end is a Single-Page Application (SPA) written in JavaScript, using Backbone.js, Marionette.js, and a number of JavaScript plugins.

On the back-end, we have a suite of RSpec unit specs and Capybara acceptance specs to make sure everything works on the back-end. When I joined Contactually in September 2014, the front-end code didn’t have any unit tests – everything was tested manually by traditional QA processes.

In our app, there’s a lot of business logic between different objects.

  • A User has many Contacts and Groupings
  • A Contact can be in many Groupings, but only certain subtypes of Grouping, such as Buckets
  • A Grouping can belong to many Users
  • A Domain can have many Users, and many Groupings
  • Etc.

Almost all of this logic is spec’ed out and verified by RSpec and Capybara specs, but a lot of the same business logic on the client-side was not, which led to hard-to-find bugs in the Backbone app. As an example, after creating a new Contact, the UI didn’t update properly despite the new Contact was created successfully on the back-end.

I set up a Test-Driven Development (TDD) environment for testing the various Views, Models, Controllers, and Mixins that make up Contactually’s Backbone app. Going forward with new UI features or bug fixes, any developer can set up a JavaScript spec with minimal overhead and keep the TDD ball rolling.

Since Contactually is a Rails app, I chose a JS testing framework that’s similar in style to RSpec. I chose Mocha (and Chai) over alternatives such as QUnit or Mocha because of much simpler setup and configuration and much easier asynchronous testing support. Check out this link for a more detailed comparison between Mocha, QUnit and Jasmine.

Konacha

Konacha is a Rails engine for testing JavaScript in a Rails app. It provides the Mocha test framework and Chai assertion as defaults, but you can plug in other frameworks. For the purposes of this tutorial, we’ll stick with Mocha and Chai.

Rails Assets

Rails Assets is a gem source to install front-end dependencies, such as Underscore, jQuery, Backbone, and the like, by specifying them in the app’s Gemfile. We’ll use rails-assets gems to install the necessary front-end dependencies for this tutorial.

Handlebars Assets

Instead of using Backbone’s built-in template engine with Underscore, we’ll use Handlebars. The handlebars_assets gem has the most recent stable version of the Handlebars JS library and will allow the Rails app to precompile any Handlebars template files (ending in .hbs) for use by Backbone or Marionette views.

Poltergeist

Poltergeist is a webdriver that both Capybara and Konacha can use to run their respective specs in PhantomJS. It’s the preferred webdriver for Konacha, so we’re including it in the tutorial.

About this tutorial

This tutorial assumes that you have some experience with setting up and running a Rails app, and some JavaScript development experience. This tutorial will walk you through setting up a Rails app with the konacha, rails-assets, and handlebars_assets gems, setting up the JavaScript testing environment, and writing some JavaScript modules and Mocha specs.

You can also get the tutorial’s full app on Github.

Todot – a palindromic todo app

We’ll build a simple todo app, where each todo is called a Dot, and a list of todos is called a Dots.

For this tutorial, we’ll be using the following tools and their respective versions:
* ruby 2.1.5
* rails 4.2.0
* phantomjs 1.9.7

Setting up the app

gem install rails -v 4.2.0
rails new todot
cd todot
git init .
git commit -am "initial commit"

konacha

Create a spec/javascripts folder in the root of the app folder. Here is where you’ll keep all your JS spec files. All spec files end in _spec.js (or _spec.coffee if you’re using CoffeeScript.)

To configure Mocha when running the JS specs using Konacha, create a spec_helper.js file, similar to the spec_helper.rb file for Ruby RSpec.

We’ll use the following configuration for the todot app.

mocha.ui('bdd');

// ignore the following globals during leak detection
mocha.globals(['Backbone', 'JST']);

// Show stack trace on failing assertion.
chai.config.includeStack = true;

ENV = {
  TESTING: true
};

beforeEach(function() {
  window.SANDBOX = $("#konacha");
});

The SANDBOX is a div that Konacha sets up in an iframe for each spec. We’ll use the SANDBOX for one of our Marionette View specs, dots_view_spec.js.

Create an initializer for Konacha at config/initializers/konacha.rb with the following:

if defined?(Konacha)
  Konacha.configure do |config|
    config.spec_dir = 'spec/javascripts'
    config.stylesheets = %w{application}
    config.javascripts = %w(chai konacha konacha/iframe)
    config.driver = :poltergeist
  end
end

When running Konacha’s browser test environment, each spec will be run in its own separate iframe inside the browser. Rather than each iframe have its own copy of all the JavaScript libraries required to run its spec, we can use a single JavaScript manifest file with all the requires we need, and each iframe will use this file. This can significantly speed up when running Konacha.

Create app/assets/javascripts/konacha/index.js with the following:

//= require jquery
//= require underscore
//= require backbone
//= require backbone.marionette
//= require handlebars.runtime
//= require handlebars-helpers/src/helpers.js

In config/initializers/konacha.rb, the konacha item in the line config.javascripts = %w(chai konacha konacha/iframe) refers to the above manifest file.

handlebars_assets

By default, the handlebars_assets gem will look for HandlebarsTemplates in any JavaScript files that will use a pre-compiled template. For sake of brevity in our code, we’ll use the token JST instead.

Create the following at config/initializers/handlebars_assets.rb:

if defined?(HandlebarsAssets)
  HandlebarsAssets::Config.template_namespace = 'JST'
end

Let’s say you have a Handlebars template, app/assets/javascripts/templates/dots/dot_view.hbs. To use this template in a Marionette view:

var DotView = Backbone.Marionette.ItemView.extend({

  template: JST['dots/dot_view'],
  model: Dot,
  className: 'dot-view',
  ...
});

handlebars-helpers

This gem has a set of simple and common Handlebars helpers that we can use in our Handlebars templates. Its gem is included in the Gemfile in the repo and its JavaScript file is included in app/assets/javascripts/konacha/index.js.

Running konacha

You can run your JavaScript specs in Konacha one of two ways: either in the browser, or on the command-line using PhantomJS.

$ bundle exec rake konacha:run # at the command line
$ bundle exec rake konacha:serve # in the browser at http://localhost:3500

You can configure Konacha to run on a different port in the browser, such as 9001, by setting config.port = 9001, in config/initializers/konacha.rb. The same applies for configuring the port for running via the command line: set it using config.runner_port.

dot.js and dot_spec.js

Each Dot in the app will have the following:
* a name
* a priority – represented as an integer
* a status – either 'new' or 'complete'

Here’s our Dot Model at app/assets/javascripts/models/dot.js:

var Dot = Backbone.Model.extend({

  defaults: {
    name: 'Dot',
    priority: 1,
    status: 'new'
  },

  mark_as_complete: function() {
    this._mark_as('complete');
  },

  mark_as_new: function() {
    this._mark_as('new');
  },

  _mark_as: function(status) {
    this.set('status', status);
  }

});

Here’s its spec at spec/javascripts/models/dot_spec.js:

//= require spec_helper
//= require models/dot

describe('Dot', function() {

  var subject;

  beforeEach(function() {
    subject = new Dot();
  });

  it('has the correct defaults', function() {
    expect(subject.get('name')).to.eq('Dot');
    expect(subject.get('priority')).to.eq(1);
    expect(subject.get('status')).to.eq('new');
  });

  it('can update its status', function() {
    subject.mark_as_complete();
    expect(subject.get('status')).to.eq('complete');

    subject.mark_as_new();
    expect(subject.get('status')).to.eq('new');
  });

});

Run bundle exec rake konacha:serve and go to http://localhost:3500/models/dot_spec?grep=Dot. Click on the little arrows next to each spec’s line to show the actual JavaScript code for that spec.

dot_spec in browser

Collection and View Specs

Check out the tutorial’s repository for the rest of the todot JavaScript modules and specs, and get rolling with JavaScript TDD in Rails 4 & Konacha!

The following two tabs change content below.
Andrew Curry
Hi, I'm Andy, a senior engineer at Contactually in Washington, DC. I blog every now and then about JavaScript. I'm a real human bean.
Andrew Curry

Latest posts by Andrew Curry (see all)

Submit a Comment

Your email address will not be published. Required fields are marked *