Stubbing with Moksi

Manfred Stienstra, 20 Mar 2009, 00:02 in testing and javascript (edit).

In the summer of 2008 I wrote a small mocking and stubbing library for JavaScript called Moksi. I used it a little bit in a project and then forgot about it. When we picked up the project again a few weeks ago I was happy to rediscover Moksi. Because there isn’t much written about testing in JavaScript, I figured it might be a good idea to share some tips and tricks.

We do most of our testing with unittest.js which was developed as part of the testsuite for script.aculo.us. If you’re not familiar with it: Thomas Fuchs did a nice presentation at RailsConf 2006 about it. You might want to read up if you don’t know what I’m taling about.

So now you have assertions galore, but what do you do when you don’t want to recreate an entire scenario in the DOM just to test one or two things? You stub them.

Suppose you want to test the following function code:
var Autosave = Class.create({
  initialize: function(form) {
    this.form = $(form);
  },
  
  installObserver: function() {
    this.form.observe('change', function() {
      this.saveAsDraft();
    });
  },
  
  saveAsDraft: function() {
    form.request({ parameters: { draft: true }});
  }
});

The problem might be that you want to make sure the form is saved as draft, but you don’t actually want to do the AJAX request.

new Test.Unit.Runner({
  setup: function() {
    // Create a form in the DOM and initialize an Autosave.
    $('sample').innerHTML = '<form action="" id="testform"><input type="checkbox" value="money" name="get" /></form>';
    
    this.autosave = new Autosave('testform');
  },
  
  teardown: function() {
    Moksi.revert();
  }
  
  testFormIsSavedAsDraftWhenChanged: function() { with(this) {
    // Stub Form.request on the form and record its requests
    Moksi.stub(this.autosave.form, 'request', function(options) {
      this.autosave.form.requests = this.autosave.form.requests || [];
      this.autosave.form.requests.push(options);
    }.bind(this));
    
    this.autosave.saveAsDraft();
    
    self.assertEqual(1, this.autosave.form.requests.length);
    
    var requestOptions = this.autosave.form.requests.last();
    self.assertNotNull(requestOptions.parameters);
    self.assertEqual(true, requestOptions.parameters.draft);
  }}
});

Notice the Moksi.revert() call? That makes sure all your stubs get reverted after running each test, which is really helpful if you only want to stub a function in one test and use the normally defined function in all the others.

3 comments

Rails 2.3 is faster

Manfred Stienstra, 06 Mar 2009, 15:54 in ruby on rails and performance (edit).

This morning I was deploying an application originally built on Rails 1.1.5 which we’ve recently ported to Rails 2.3. It’s an introductory online statistics course aimed at people who study psychology and social sciences. Most of the pages serve static content straight from the database, so we were never really concerned about performance.

After I finished deploying the application I was tailing the production logs and I noticed that the application took a lot less time completing page requests than before. Just for fun I decided to run some benchmarks on my development box¹.

1.1.5 2.3.1
Login 47.3㎳ (σ = 24.5) 12.1㎳ (σ = 10.2)
Lesson index 177.6㎳ (σ = 30.4) 60.0㎳ (σ = 25.7)
Lesson show 66.5㎳ (σ = 36.1) 19.0㎳ (σ = 15.3)

Please don’t interpret these numbers as Rails 2.3 is three times faster, because that’s not what these numbers mean. However, when you port you application to Rails 2.3 your server load will very likely go down.

¹ Mac Pro, 2 x 2.8 Ghz, 4 GB 800 Mhz DDR2. Apache with Passenger 2.1.0 & MySQL 5.1. Applications were running in production. Benchmarks were generated with Apache Bench with concurrency 5 and 2000 requests.

No comments yet