Speeding Up the Konacha JavaScript Testing Framework with Views

Konacha can only run as fast as Rails can serve it. Konacaha’s readme suggests including the JavaScript you need to run your tests in a JavaScript spec helper, but this can result in excessively expensive asset compilation. Replacing Konacha’s iframe view with your own implementation can yield a substantial improvement in execution time for a large test suite.

Moving to Konacha

Recently the Iora engineering team decided to start using Konacha as our JavaScript test runner after a few of us had started using Konacha in extra-curricular projects and fallen in love with Mocha, Chai, and the bustling Chai ecosystem. In fact, we loved Konacha so much that we decided to forgo our usual, conservative approach to tool adoption (port it as you touch it) and instead embarked on “Chaimageddon”: an afternoon devoted to all hands on deck porting of Jasmine tests to Chai. Chaimageddon was a big success (we migrated over half of our very large JavaScript test suite), and pull requests full of ported tests came flooding in.

Everything looked great, until we merged…

bundle exec rake konacha:run

A few seconds pass and I decide to grab a cup of coffee; I return and it’s still running. Ten minutes later, it finishes. Oooof. We had only ported about half of our tests and already the Konacha run was more than twice as long as the Jasmine run had been.

Tailing log/development.log revealed the following:

Processing by Konacha::SpecsController#iframe as HTML
  Parameters: {"name"=>"OptionPresenterSpec"}
  Rendered /Users/myke/.rvm/gems/ruby-1.9.2-p320@icis/gems/konacha-2.6.0/app/views/konacha/specs/iframe.html.erb (1599.6ms)
  Completed 200 OK in 1657ms (Views: 1605.3ms | ActiveRecord: 0.0ms)

Since the iframe endpoint gets hit once per spec, there was a ≈ 1500ms request for every spec file.

After profiling the iframe action in the Konacha engine the source of the sluggishness became obvious: we were requiring our spec_helper.js in each spec file, which looked something like this (note that this spec_helper contains requires for all supporting JavaScript, as suggested in the Konacha readme):

#= require application
#= require sinon
#= require sinon-chai
#= require js-factories
#= require chai-backbone
#= require chai-jquery
#= require chai-as-promised
#= require chai-null

beforeEach ->
  # universal test setup

afterEach ->
  # universal test cleanup

Since we were running these tests in the rails development environment, for each spec file, we would compile all of the assets shared across the entire suite.

Defining a custom iframe view

When these shared assets are required through the spec helper, sprockets is forced to compile them each time, as it follows the requires from the spec file itself, to the spec helper, and up the rest of the (potentially complex) require tree. An easy and cheap way to prevent needlessly re-compiling these assets each time the endpoint is hit is to require them in the view itself, rather than in the spec helper. Since Konacha is a rails engine, this is as easy as defining your own iframe.html.erb view in app/views/konacha/specs/. We simply replaced moved all of our required JavaScript from require statements in the spec helper to the view itself, so that:

<%= javascript_include_tag "chai", "konacha/iframe", debug: false %>


<%= javascript_include_tag "chai", "konacha/iframe", "application", 'sinon', 'sinon-chai', 'js-factories', 'chai-backbone', 'chai-jquery', 'chai-as-promised', 'chai-nill', debug: false %>

When all was said and done we had added the following app/views/konacha/specs/iframe.html.erb to our main app (almost identical to the one in konacha):

<!doctype html>
<html data-path="<%= @spec.path %>">
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    <title>Konacha Tests</title>

    <% @stylesheets.each do |file| %>
      <%# Use :debug => false for stylesheets to improve page load performance. %>
      <%= stylesheet_link_tag file, :debug => false %>
    <% end %>

    <%= javascript_include_tag "chai", "konacha/iframe", "application", 'sinon', 'sinon-chai', 'js-factories', 'chai-backbone', 'chai-jquery', 'chai-as-promised', 'chai-nill', debug: false %>
    <%= javascript_include_tag @spec.asset_name %>

And spec/konacha/spec_helper.js.coffee looked like:

# Notice there are no requires here!

beforeEach ->
  # universal test setup

afterEach ->
  # universal test cleanup

The Result

The benchmarks speak for themselves:

Run #   Time (m:ss)   Description
1       8:12.92       The existing implementation (Konacha 2.6.0)
2       10:47.82      The existing implementation (Konacha 2.6.0)
3       10:43.25      The existing implementation (Konacha 2.6.0)
4       9:58.47       The existing implementation (Konacha 2.6.0)
Average 9:55.615

1       2:40.99       Move requires from spec helper to iframe.html.erb (Konacha 2.6.0)
2       2:24.90       Move requires from spec helper to iframe.html.erb (Konacha 2.6.0)
3       1:09.45       Move requires from spec helper to iframe.html.erb (Konacha 2.6.0)
4       1:08.0        Move requires from spec helper to iframe.html.erb (Konacha 2.6.0)
Average 1:50.835

Benchmarks run using time bundle exec rake konacha:run

We plan on opening a pull request with a more elegant solution: require the spec_helper (or maybe konacha/manifest) from Konacha’s iframe.html.erb if they are defined, allowing the same performance boost without requiring a monkey patch of sorts (after all, as Konacha changes this view may change, or be eliminated entirely, leaving us in the dust!). But for now we’re happily running fast builds and back to loving Konacha.