Dr Nic

Autotesting Javascript in Rails

I used to love Javascript so much that it would scare me. Why? Because I used to never write any tests. No unit tests. No integration tests (e.g. Selenium). Why? I didn’t know how. Not properly. I didn’t know how to write Javascript unit tests with an autotesting tool, like ZenTest’s autotest command.

And now…

javascript-testing-intro.jpg

I am now free to write as much Javascript as I like knowing forever I’ll write Javascript tests first, and Javascript in libraries second. TDD for Javascript beckons!

Below is a “getting started” tutorial, a helpful autotesting plugin, and hints about a TextMate bundle for Javascript Unit Testing and a future Peepcode on Javascript Unit Testing.

Installation

Fundamentally, the ideas here are app server-neutral. We’re using the unittest.js library from scriptaculous. But here I’ll show/demo the Rails plugins that make this all uber easy to get started.

Firstly, create a blank rails app, or use an existing app. The tutorial should be non-invasive – just a few “demo” files to delete when you’re done. This tutorial is independent of your app.

ruby script/plugin install javascript_test
mkdir test/javascript
ln -s ../../vendor/plugins/javascript_test/assets/ test/javascript/assets

For Windows people copy vendor/plugins/javascript_test/assets into test/javascript/assets, as you don’t have symbolic links, which is sad.

At this stage, you can add and commit these files to your project. The rest of this article is destructible demo files and tests.

Creating javascript tests

Let’s say

$ ruby script/generate javascript_test maths
    exists  test/javascript
    exists  public/javascripts
    create  test/javascript/maths_test.html
    create  public/javascripts/maths.js

maths.js is just a blank javascript file. The important file is maths_test.html. Its packed with lots of “getting started” goodies.

Open test/javascript/maths_test.html and find the testTruth method.

testTruth: function() { with(this) {
  assert(true);
}}

This is an example of a javascript test method, with a sample assert call. It has the same api as the Ruby test::unit assert call. Thomas Fuchs’ presentation Adventures in JavaScript testing gives a good rundown of the available assert methods (and the BDD style syntax that is available). I’m also preparing a TextMate bundle to generate the assert calls, with the same “as”, “ase”, “asnn” tab completions as the Ruby bundle. It makes me happy when using it.

Running unit tests

The most holistic way to run your javascript unittests, is against all the browsers you have on your local machine. The javascript_test plugin comes with a rake task for this: test:javascripts

$ rake test:javascripts
(in /Users/nicwilliams/Documents/rails_apps/imindi_theme)
/test/javascript/maths_test.html on Safari: SUCCESS
/test/javascript/maths_test.html of Firefox: SUCCESS
Skipping Internet Explorer, not supported on this OS
Skipping Konqueror, not supported on this OS

In the browsers you’ll see:

javascript-unit-test-file.jpg

It runs all your tests on all browsers. That’s great for a Continuous Integration process, but for me, coming from the ZenTest world of autotest, I only want to run those tests for which something has changed.

I want continual feedback. Small change, run small test. autotest gives me that for Ruby, but rake test:javascript doesn’t.

Autotesting javascript tests

On my Mac, I’ve set up something as a starting point for autotesting javascript tests, based on the “small change, run small test” principle.

$ ruby script/plugin install http://drnicutilities.rubyforge.org/svn/plugins/javascript_test_autotest/
.
.
Edit config/javascript_test_autotest.yml for the browser(s) to use for autotesting.

Open config/javascript_test_autotest.yml and uncomment the browser(s) you want to autotest with. I autotest on Safari on the Mac, even though I develop/debug on Firefox, because it reloads each page in the same tab, which I like, and I’m having problems getting this to work into Firefox at all.

My yml file looks like:

browsers:
  safari: '/Applications/Safari.app/Contents/MacOS/Safari'

So, the current version is known to work for Mac OS X and Safari. If you get this plugin working on different O/S and browsers, let me know below or please submit a patch to http://groups.google.com/group/drnicutilities

From a command line:

$ script/js_autotest
Watching public/javascripts/controls.js, public/javascripts/application.js, test/javascript/maths_test.html, public/javascripts/effects.js, public/javascripts/dragdrop.js, public/javascripts/prototype.js

Files: 6

Let’s TDD with js_autotest

In test/javascript/maths_test.html, replace testTruth, with the following and save:

// Maths class should exist
testMathsClassShouldExist: function() { with(this) {
  assert(Maths, 'Where is Maths?');
}},

js_autotest will automatically load maths_test.html into your browser, and its tests executed.

javascript-testing-failure.jpg

Now, let’s fix the problem by creating the Maths javascript class. In public/javascripts/maths.js, add:

var Maths = Class.create();
Maths.prototype = {
  initialize: function() {

  }
}

js_autotest will again automatically reload maths_test.html into your browser, and its tests executed.

javascript-testing-success.jpg

Yay for TDD and Yay for “small change, run small test”.

Peepcode

I love Geoffrey’s Peepcodes. Geoffrey has all my money in Peepcode credits. More importantly, Geoffrey’s due to give birth this year to #1 child. Geoffrey, who works from home, is blissfully unaware that soon he’ll be a full-time father and part-time Peepcode author. This could mean fewer peepcodes on important peepcode-worthy topics.

I always wished there was a Peepcode on Javascript Unit Testing. It would take many blog posts to cover as much as can be covered in a single 60 minutes peepcode. So, we’re writing/videoing one.

As a Javascript developer [has anyone ever heard of a Javascript Users Group?] let me know what specifically you’d like covered in this video, so I don’t accidently miss something.

[optional] Install some patches

There was a section here about some patches that could be applied. These have now been applied, and javascript_test plugin has been updated with prototype 1.6.0.1. Life is good.

Related posts:

  1. Instant new Rails applications with the App Scrolls When I start a new project I want to start...
  2. Using CoffeeScript in Rails and even on Heroku I’m pretty excited about CoffeeScript as a clean-syntax replacement for...
  3. First look at rails 3.0.pre This article is out of date in some aspects....
  4. Rails themes can remember things I was getting annoyed at having to remember all the...
  5. Install any HTML theme/template into your Rails app Have you ever even bothered to Google for “rails...

38 Responses to “Autotesting Javascript in Rails”

  1. James H. says:

    Very cool! Thank you for this.

  2. How to you test events? For instance making sure that an ajax request is triggered and the proper variables are passed?

    Is there a mocking framework to use with js tests?

    Mootools as a cool rspec type syntax http://blog.mootools.net/2007/10/20/what-s-new-in-1-2-spec-runner How can we have something similar with Prototype?

  3. Dr Nic says:

    @Matt Aimonetti [via] – the specs do look sweet

  4. Martin Ström says:

    Great!
    Remember that the asset files in javascript_test (prototype.js, unittest.css|js) haven’t been updated for a while and are older than the Rails 2.0 files.
    When I’m using javascript_test I usually symlink my javascript_test assets to files in my public dir so my tests work against the very same version of Prototype (living in public)

  5. Dr Nic says:

    @Martin Ström [via] – yeah, I noticed that with prototype etc, but hadn’t gone hunting the latest unittest.js. I’ll go write up a patch for the javascript_test plugin now.

    Ticket 10694

  6. Dr Nic says:

    @Martin Ström [via] – thx – I’ve also patched the scriptaculous unittest.js version (#10695) with some fixes.

    Patches lack instant gratification of “svn ci -m ‘…’” :)

  7. Jesse says:

    Check out JSSpec for some more Javascript BDD action:

    http://jania.pe.kr/aw/moin.cgi/JSSpec

  8. foca says:

    @Matt: Well, there’s js-spec, which I’ve been working for a while now :)

    http://js-spec.googlecode.com

    The syntax is different to the mootools lib: it is much more rspec-ish :)

    I’m currently working on more matchers (for more javascripty things, like event testing and such) and thinking on a cool way to do mocks and stubs. Any ideas and feedback are well appreciated :)

    BTW, Nic, *please*, fix the tabindex of your openId field, I hate that tabbing out of it takes me to the search at the top of the page :)

  9. [...] Nic has written a tutorial on testing JavaScript in Rails using a new javascript_test [...]

  10. [...] Nic has written a tutorial on testing JavaScript in Rails using a new javascript_test [...]

  11. [...] Dr Nic » Autotesting Javascript in Rails (tags: javascript testing rubyonrails plugins) Tags: Found Objects [...]

  12. @Matt Aimonetti [via] – You can simulate events with:

    Event.simulateMouse(element,’click’);

  13. Scott Becker says:

    @Dr Nic: Nice! I noticed a couple things –
    “ruby script/generate javascript_test maths” did not create “public/javascripts/maths.js” for me. From the generator, it doesn’t look like it would. Is that in a not-yet-applied patch to the plugin?

    Also, the rake task for running the tests should be rake test:javascripts (plural).

  14. Scott Becker says:

    @Dr Nic: Oh, and now I read the bottom where you say your depending on patches. Might I suggest putting that in the “installation” step, for those of us who recklessly dive in and try it as we read it? :)

  15. @foca looks sweet, it looks like it’s prototype based. Does it work with any JS framework? Did you test it with lowpro or another UJS lib?

  16. [...] Dr Nic » Autotesting Javascript in Rails (tags: javascript testing autotest rails tdd rubyonrails ruby js) [...]

  17. [...] Dr Nic » Autotesting Javascript in Rails (tags: AJAX BDD development javascript programming Ruby TDD tutorial ZenTest) [...]

  18. I am really a big fan of javascript testing, but not in the browser. I always forget to run them. We are working on a rails javascript test suite using crosscheck and jsSpec, which looks very promising.

  19. Dr Nic says:

    @Bart ten Brinke [via] – that’s why I created the js_autotest script – so it ran the tests automatically. In safari, it reuses the same tab; so it can sit nicely on one side of my monitor next to the text editor.

  20. @Dr Nic. I understand, but I don’t want to rely on a browser that might or might not be installed / configured correctly. And I want to be able to run it in autotest and as a svn checkin hook.

  21. Dr Nic says:

    @Bart ten Brinke [via] – absolutely, that’s what I’d love too. I’m yet to investigate crosscheck yet, etc, but its also on my todo list.

  22. I had idea a year ago or so to make a Rails plugin that would let the JavaScript-tests run some of the tests with the full output from the rails stack.
    Let say you have a rails app app with some javascript being easily tested classes like Array extensions or something like that. But how about the parts where the JS uses the generated DOM tree as “input”? It isn’t very DRY to copy the app’s generated output to the tests as html so my idea was to specify an action or url and then let the plugin insert the javascript files needed to let one run tests on the output generated by Rails (pretty much like how the functional tests work right now).

  23. Dr Nic says:

    @Martin Str̦m [via] РI under the desire to ensure the view being tested is the same, and the desire to be DRY about it.

    My solution is to generate sample HTML, built from views, into my test/javascript/samples folder, and then load them into the html unittest page via synch GET request (synch Ajax request). I create the samples manually or via a rake task. Its very tricky convincing an ActionView::Base to render a template without a full request stack in place, but I’ve got a little helper method that tricks out a pretend request+controller.

    I’ll post about it later when its all cleaned up, and the loadSample() JS helper patch is accepted into unittest.js.

    But, with generated sample HTML for tests, I think that gives me enough DRYness.

  24. [...] Dr Nic » Autotesting Javascript in Rails (tags: javascript testing rails rubyonrails tdd ruby ajax howto framework development ror) [...]

  25. maruis says:

    Great intro into tdd’ing js. I am looking forward to the peepcode screen cast. I would definably be interested when it’s released.

    Good news to hear Jeffrey is going to be a dad. He will enjoy the baby a lot…

    Cheers…

  26. [...] Autotesting JavaScript in Rails – Dr. Nic show us how to use the javascript_test plugin together with the ZenTest autotest script. [...]

  27. Dr Nic says:

    @Scott Becker [via] – sorry Scott :) The patches have all been applied now so future readers can live in a happy place that you were denied :)

  28. Mark says:

    Windows Users have symbolic links! It’s just not well publicized:
    http://shell-shocked.org/article.php?id=284
    I use this program to easily create them:
    http://schinagl.priv.at/nt/hardlinkshellext/hardlinkshellext.html

    Looking forward to the upcoming peepcode episode!

  29. [...] tutoriais e artigos técnicos interessantes que chamaram nossa atenção foram aqueles chamados Autotestando JavaScript em Rails, de Dr. Nic, que explica como testar nossos scripts em JavaScript desde um script em Rails, [...]

  30. massimiliano says:

    Great work!!!

    On Mac OS X Leopard I had to change the first line of file script/rstakeout to

    #!/usr/bin/ruby -w

  31. [...] think my next step is to take another look at what Thomas Fuchs has been up to, and also this rails-based effort from Dr Nic. We’re using the prototype.js framework anyway, so this seem like it would be a [...]

  32. Ed Spencer says:

    Thanks for this, I’ve got a Windows solution for you.

    I had to take the contents of js_autotest and chuck it in with the javascript_test_autotest_tasks.rake, as well as change rake to rake.bat – like this:

    # javascript_test_autotest_tasks.rake
    namespace :test do
    namespace :recent do
    #untouched
    end

    task :javascript_autotest do
    system(‘ruby script/rstakeout “rake.bat test:recent:javascript” test/javascript/*_test.html test/javascript/samples/*/* public/javascripts/*.js’)
    end
    end

    Then obviously rake test:javascript_autotest from windows command line and everything seems to work pretty well. I’ve only tested it with a few trivial examples but it detects the right changes and runs the right tests so it looks promising.

    It’s pretty trivial but I’ll submit a patch after I get back from the pub.

  33. Jeremy says:

    Cheers for tutorial Dr Nic, V good for noob like myself.
    To make this work in Ubuntu i had to change line 1 of script/rstakeout to:
    #!/usr/bin/env ruby

    and to javascript_test_autotest.yml I added:
    firefox: ‘/usr/lib/firefox-3.0.1/firefox.sh’

    which seems to work fine :-)

  34. Dr Nic says:

    @Jeremy – check which version of newjs you’re using (gem list newjs) as I think the #! line is fixed in 1.4.1

  35. [...] Nic has a nice blog post on the subject which I’d strongly encourage you to check [...]