About 2 years ago I wrote about how to set up your Rails app for JavaScript testing, so I figured it was time for an update. It’s pretty much the same as the previous post, but it assumes you’re using Rails 5 and newer versions of Teaspoon and Magic Lamp.
And away we go!
Setting up Teaspoon
Teaspoon is JavaScript test runner for Rails that runs in continuous integration (CI) and the browser for easy debugging. You’ll have to decide whether to Jasmine, Mocha, or QUnit as your actual JavaScript test framework (When it’s up to me, I go with Mocha). We want to add the Teaspoon gem to the test and development groups in our Gemfile:
group :development, :test do gem "teaspoon-mocha" # or "teaspoon-jasmine" or "teaspoon-qunit" end
After you bundle install
you’ll want to run:
$ rails generate teaspoon:install
That command will generate spec/teaspoon_env.rb
and spec/javascripts/spec_helper.js
files; both contain a ton of really helpful comments that explain how to configure Teaspoon in all kinds of ways. For now, let’s just make sure we have all of the JavaScript we need in our spec/javascripts/spec_helper.js
.
Teaspoon comes with some handy JavaScript testing libraries like Expect, Sinon, and Chai we can pull in using Sprockets comments/directives. Since we’re using Mocha we’ll need to pull in an assertion library (Chai tends to be my go to) which means fiddling with some of the comments:
// Helpful comments... //= require support/chai // More helpful comments... window.expect = chai.expect;
Note: Make sure you didn’t delete //= require application
or else our code won’t be loaded into the test environment!
Now we can get our first tests going by adding a file called spec/javascripts/test_spec.js
with the following:
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 app we should see:
Starting the Teaspoon server... Puma starting in single mode... * Version 3.6.0 (ruby 2.3.1-p112), codename: Sleepy Sunday Serenity * Min threads: 5, max threads: 5 * Environment: test * Listening on tcp://127.0.0.1:51170 Use Ctrl-C to stop Teaspoon running default suite at http://127.0.0.1:51170/teaspoon/default .F Failures: 1) Testing is not going so smoothly Failure/Error: expected true to equal false Finished in 0.01300 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 localhost:3000/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 as well as arbitrary strings and JSON 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).
Setting up Magic Lamp
First we’ll add Magic Lamp to our test and development groups in our Gemfile
:
group :development, :test do gem "teaspoon-mocha" gem "magic_lamp" # just added end
Then after we bundle install
we need to mount Magic Lamp in our config/routes.rb
Rails.application.routes.draw do mount MagicLamp::Genie, at: "/magic_lamp" if defined?(MagicLamp) # ... end
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 //= require magic_lamp
to your spec/javascripts/spec_helper.js
. It should be similar to:
// 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? %><% end %>prohibited this order from being saved:
<% 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.fixture do order = Order.new(name: "invalid name") order.valid? render partial: "orders/form", locals: { order: order } 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. Also, if your template depends on instance variables, you can just assign them in the Magic Lamp fixture
block just like if you were in a Rails controller.
Summary
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 a JavaScript array or object is like another (since === is very strict and
==
can’t be trusted) - chai-jquery: adds some helpful assertions for jQuery objects
DevMynd is innovation firm in Chicago and San Francisco with practice areas in digital strategy, human-centered design, UI/UX, and custom mobile and web application development. The firm’s mission is to help its clients use technology to solve meaningful problems that have a profound impact on life, society and business.