Setting Up Your Rails App For JavaScript Testing

Note: I wrote this post back in 2014 and have since written an updated version. If you’re starting a fresh project, check it out!

Setting up Teaspoon

Teaspoon is a JavaScript test runner for Rails that runs
in ci and the browser for easy debugging. It also makes it easy to manage test dependencies.

You know the drill: Add this to your Gemfile in the test
and development groups:

gem "teaspoon"

Then bundle install as usual. Next up you’ll want to
run:

$ rails generate teaspoon:install --framework=mocha

That command will generate your Teaspoon config, most notably a spec/teaspoon_env.rb and a spec/javascripts/spec_helper.js. I’m specifying mocha here as my test framework but Teaspoon
supports Qunit and Jasmine (which is the default) too.

Side note: Teaspoon only supports Jasmine 1.3, not 2.0. There’s work being done to address this though, and you can keep tabs on it here.

Anyway, both files will be full of glorious comments explaining all the wonderful things about Teaspoon you can configure — from the route Teaspoon mounts itself at to splitting up different suites.

If you’re running Rails 4.1, you’ll also want to add this to your development.rb:

Rails.application.config.assets.precompile += [
  "teaspoon.css",
  "teaspoon-mocha.js",
  "mocha/1.17.1.js"
]

If you’re using Qunit or Jasmine you’ll need to swap mocha/1.17.1.js for either jasmine/1.3.1.js or qunit/1.14.0.js.

All of the JavaScript you want in your tests needs to be required in your fancy new spec_helper.js like so:

// Lots of helpful comments...
//= require application
//= require support/chai
window.expect = chai.expect;

We’re able to pull Chai in for our assertions without downloading any files or adding a gem because Teaspoon comes preloaded with it under support. If you want any other test helpers, just add them to your spec/javascripts directory and pull them in the same way we pulled in Chai (the path will be relative
to spec/javascripts). I usually throw in Sinon (Teaspoon comes preloaded with this too), sinon-chai, chai-fuzzy, and chai-jquery since they all come in handy eventually.

Now if we add a spec file under spec/javascripts called test_spec.js, we can write:

describe("Testing", function() {
  it("is going so smoothly", function() {
    expect(true).to.equal(true);
  });

  it("is not going so smoothly", function() {
    expect(true).to.equal(false);
  });
});

Now if we run rake teaspoon or simply teaspoon from the root of our Rails application we should see:

Starting the Teaspoon server...
Teaspoon running default suite at http://127.0.0.1:50173/teaspoon/default
.F

Failures:

  1) Testing is not going so smoothly
     Failure/Error: expected true to equal false

Finished in 0.00500 seconds
2 examples, 1 failure

Failed examples:

teaspoon -s default --filter="Testing is not going so smoothly"

Bam! And if we wanted to debug our failing test, we can simply run rails server and visit /teaspoon/default and watch our tests run. Now you can crack open Chrome
developer tools, play with the console, set break points and all of that good stuff.

Testing that true is true is all well and good, but we’re writing JavaScript to manipulate the DOM, so let’s
get some html in there.

You could put some fixture files at spec/javascripts/fixtures and load them via Teaspoon, but we don’t want to have to make sure our fixture files are always up to date (if you really
want to, you can read more about that here). Instead we’re going to make sure our fixtures’ markup is always
up to date with Magic Lamp.

What is Magic Lamp?

Magic Lamp provides an easy way to get your Rails templates into your JavaScript tests. By testing your JavaScript against your actual templates and partials, your tests will break when your markup changes in a way that breaks your code. This beats the alternatives of creating tons of fixture files or stubbing out your markup with jasmine-fixture. It’s also great if you want to test your views with jQuery instead of assert_select (gross). Keep reading to see an example of Magic Lamp in action (or check out the readme here).

Setting up Magic Lamp

You’ll need to add this to your Gemfile in the test and development groups:

gem "magic_lamp"

Then bundle install as usual. Next up you’ll want to add this:

Rails.application.routes.draw do
  # ...
  mount MagicLamp::Genie, at: "/magic_lamp" if defined?(MagicLamp)
  # ...
end

to your config/routes.rb. Be sure to add it to the top of draw block if you have any catchall routes that might catch requests to /magic_lamp first.

Next you’ll want to add this:

//= require magic_lamp

to your spec_helper.js. It should look something like this now:

// Lots of helpful comments...
//= require application
//= require support/chai
//= require magic_lamp
window.expect = chai.expect;

Now Magic Lamp is ready to go.

So let’s say you have this scaffolded up form in your app:

<%= form_for(@order) do |f| %>
  <% if @order.errors.any? %>

<%= pluralize(@order.errors.count, “error”) %> prohibited this order from being saved:

        <% @order.errors.full_messages.each do |message| %>

      • <%= message %>

 

<% end %>

<% end %>

<%= f.submit %>

<% end %>

And you have some JavaScript that will make the error messages fade out that you want to test.
Create a file called magic_lamp.rb in spec/javascripts that contains:

MagicLamp.register_fixture do
  @order = Order.new(name: "invalid name")
  @order.valid?
  render partial: "orders/form"
end

Then in your JavaScript tests you can add:

describe('Order form errors', function() {
  beforeEach(function() {
    MagicLamp.load("orders/form");
  });

  it('depends on the dom', function() {
    expect($('#error_explanation').length).to.equal(1);
  });
});

And the html will be on the page. Now if you change that div‘s id from error_explanation
to errors the tests will break.

Summary/TL;DR

Those are the main pieces you need to get your Rails app’s JavaScript and templates ready for some serious testing. Teaspoon for running your JavaScript specs in the browser, on the command line and in ci. Magic Lamp for getting your templates and partials into your JavaScript specs so your markup changes break the tests for your dependent code and less assert_select when testing views. You can find out more about Teaspoon and Magic Lamp below (or at your local library, provided you check out this blog post there and click on the links below), along with some other helpful JavaScript testing tools.

  • Teaspoon: a JavaScript test runner for Rails (see above)
  • Magic Lamp: a way to get your Rails templates into your JavaScript specs (also see above)
  • Mocha: a JavaScript bdd testing framework
  • Sinon: a JavaScript library that provides helpful stubs and spies and can mock network requests
  • Chai: a highly pluggable JavaScript test assertion library
  • Chai plugin directory
  • sinon-chai: adds some helpful assertions for Sinon spies and stubs
  • chai-fuzzy: allows you to assert that an array or object is like another (since === is very strict and == can’t be trusted)
  • chai-jquery: adds some helpful assertions for jQuery objects

Software Development Services in Chicago and San Francisco with practice areas in digital strategy, human-centered design, UI/UX, and custom mobile and web application development.

Michael is a member of DevMynd’s software engineering team focusing on mobile apps and web development. He has been with the company since 2013.