Blog Posts tagged with: rails

May 12 2009

Comments

Blue Ridge 1.0: JavaScript Unit Testing for Rails. Scandalous!

You wouldn’t consider developing a Rails application without having a solid test suite for your Ruby code, but you’ve somehow convinced yourself to cross your fingers and look the other way when it comes to JavaScript. It doesn’t have to be that way.

Meet Blue Ridge, a Rails plugin that brings the goodness of test-driven and behavior-driven development to your unobtrusive JavaScript code in a Rails-friendly manner.

Blue Ridge

Historically, when selecting a JavaScript testing solution, you were forced to choose whether you wanted a framework that could run your tests in the browser or one that could only run your tests in a headless fashion. By providing a friendly convention-over-configuration wrapper around a collection of open source tools, Blue Ridge gives us the best of both worlds: fast, automation-friendly, and headless testing plus the ability to run your tests in whichever browser is acting up on any given day.

Last summer, Blue Ridge was just a twinkle in our eye. Last week, Blue Ridge made its public debut at RailsConf. Today, we’re pleased to announce version 1.0.

Getting Started

First, install Blue Ridge into your Rails app:

  ./script/plugin install git://github.com/relevance/blue-ridge.git
  ./script/generate blue_ridge

Blue Ridge creates a small example spec to get you started. Run your JavaScript specs to make sure that all is well so far:

  rake test:javascripts

(Hint: You can also use the `spec:javascripts` or `examples:javascripts` aliases. Blue Ridge is compatible with your testing framework of choice, be it test/unit, RSpec, Micronaut, Shoulda, test-spec, etc.).

Next, try running an individual spec. When you installed Blue Ridge, it created a spec file named “application_spec.js”. You can run it like so:

  rake test:javascripts TEST=application

Of course, you really want to generate a spec that’s specific to your app. Let’s say you want to write some tests for a JavaScript file called “public/javascripts/graphics.js”. Run:

  ./script/generate javascript_spec graphics
  rake test:javascripts TEST=graphics

Cool. We can run our JavaScript specs from the command line, and they’re fast! But you just got a bug report regarding JavaScript errors in IE6 (die already!), and you want to write some tests to prove (and then resolve) the bug. To run your spec inside a web browser, simply load the HTML fixture associated with the spec (e.g., “test/javascripts/fixtures/graphics.html” for the tests in “graphics_spec.js”) and see the results of running the tests in that specific browser.

Check out the README for more information on the directory layout and a detailed description of the various Blue Ridge components.

jQuery-Opinionated

Blue Ridge wouldn’t quite fit into the Rails ecosystem if it didn’t come equipped with a few opinions. So by default, it assumes you’re using jQuery. “application_spec.js”, which was generated when you installed the plugin, includes an example of calling jQuery functions inside a test.

  require("spec_helper.js");
  require("../../public/javascripts/application.js");

  Screw.Unit(function() {
    describe("Your application javascript", function() {
      it("provides cliché example", function() {
        expect("hello").to(equal, "hello");
      });

      it("accesses the DOM from fixtures/application.html", function() {
        expect($('.select_me').length).to(equal, 2);
      });
    });
  });

And, no, we don’t actually encourage you to write tests for standard libraries like JQuery and Prototype; it just makes for an easy demo.

Prototype-Friendly

If you prefer Prototype, no problem: “Have it your way.”

  jQuery.noConflict();

  require("spec_helper.js");
  require("../../public/javascripts/prototype.js", {onload: function() {
      require("../../public/javascripts/application.js");
  }});

  Screw.Unit(function() {
      describe("Your application javascript", function() {
          it("accesses the DOM from fixtures/application.html", function() {
              expect($$('.select_me').length).to(equal, 2);
          });
      });
  });

Put jQuery into “no conflict” mode to give the `$` function back to Prototype, require `prototype.js`, and chain any files that are dependent on `prototype.js` in the `onload` callback. Done. Your Prototype-based tests await you.

Act Now, and We’ll Throw in Mocking Support

Blue Ridge includes Smoke, a JavaScript mocking and stubbing toolkit somewhat similar to Mocha. Assume we’re testing a function called `calculateTotalCost`, and you want to ensure that it calls `calculateComponentPrice` for each given component. That test might look something like so:

  it("calculates the total cost of a contract by adding the prices of each component", function() {
    var componentX = {}, componentY = {};
    mock(SalesContract).should_receive("calculateComponentPrice")
      .with_arguments(componentX).exactly(1, "time").and_return(42);
    mock(SalesContract).should_receive("calculateComponentPrice")
      .with_arguments(componentY).exactly(1, "time").and_return(24);
    expect(SalesContract.calculateTotalCost([componentX, componentY])).to(equal, 66);
  });

Fun with TextMate

Running individual specs from the command line is an essential feature, but if you use TextMate like we do, you’d rather not have to leave your editor in order to run a spec. If this describes your development style, take the Blue Ridge TextMate Bundle for a spin.

  cd ~/Library/Application Support/TextMate/Bundles/
  git clone git://github.com/karnowski/blue-ridge-tmbundle.git Blue\ Ridge.tmbundle

Once you reload your bundles (or restart TextMate), just hit Command-R to run the currently-open spec directly from TextMate.

And be sure to check out the snippets as well. Type `it`, `des`, `bef`, or `aft`, and then press the tab key to expand into full `it` blocks, `describe` blocks, etc.

But Wait, There’s More

Want more examples? To see Blue Ridge in action inside a working Rails app, check out the Blue Ridge sample application. Among other things, the sample app includes examples of:

  • using nested `describe` functions
  • setting up per-spec HTML “fixtures”
  • stubbing and mocking functions
  • running the Blue Ridge specs as part of your default Rake task

Money Back Guarantee

Blue Ridge is guaranteed to be bug free and fulfill your every need, or your money back! Of course, in lieu of a full refund, you’re welcome to hop over to the project’s GitHub issue tracker to let us know about any issues or ideas for possible improvements. Even better, fork the repo and start hacking! If you have patches, send us pull requests.

Now, go forth and give your JavaScript code the testing love it deserves!

Jul 31 2008

Comments

Fully Headless JSSpec

Update 2009-05-12 - With the release of Blue Ridge 1.0, fully headless JavaScript testing just got a whole lot easier! Check out the plugin for the current state-of-the-art approach to unit testing JavaScript in your Rails projects.

I have been frustrated at the state of testing for JavaScript. There are a bevy of great (and not-so-great) choices for testing your JavaScript, but nothing that is current, effective, and automated. We used Crosscheck for a while, but the project went stagnant and was awfully heavy-weight besides. On top of which, it didn't offer a BDD-flavored spec syntax.

I've been a fan of JSSpec for a while, but its "runs tests only in the browser" style means that automated server-side, continuous testing was nigh upon impossible.

Then, along came John Resig and his marathon weekend of hacking to mock out the DOM API in a relatively simple JavaScript file and voila! Headless JSSpec testing for Rails apps could become a reality.

If you are interested in trying it out, first download and install Rhino 1.7R1. Make sure you put the js.jar file on a globally accessible path somewhere. Then, get a copy of my javascript_testing repo at Github (http://github.com/relevance/javascript_testing/tree/master). It is a minimal repo, with just enough to get you started. The files in the repo are:

  • env.js -- a clone of John Resig's browser environment file, with a couple of minor tweaks for Script.aculo.us support
  • test_jsspec.rake -- a couple of rake tasks for running the tests, and for integrating them with CruiseControl (will add support for other CI solutions as I can)
  • jsspec/jsspec.js -- an unmodified copy of the jsspec trunk, here for your convenience
  • jsspec/config.js -- a series of, essentially, global includes for your specs. Edit to suit your own environment
  • jsspec/consoleReportForRake.js -- an extension to JSSpec that adds an afterAll hook for reporting errors that blow up your Rake task appropriately (for continuous integration purposes)
  • jsspec/prototypeForJsspec.js -- a minor tweak to Prototype to get it working with the env.js file

To get rolling, copy everything but the rake task into RAILS_ROOT/test/javascripts. Put the rake task in RAILS_ROOT/lib. Edit jsspec/config.js to load any standard JavaScript files your project uses. Then, start writing specs. Here's an example, RAILS_ROOT/test/javascripts/spec_string.js:

load("jsspec/config.js"); 
with(Spec) {  
   describe("Prototype's String.stripTags", function() { with(this) {  

     it("should remove open and close tags", function() {  
        ("<h1>text</h1>".stripTags()).should(equal("text"));
     });

     it("should remove self-closing tags", function() {
        ("text<br/>".stripTags()).should(equal("text"));

     });
   }});
};

Specs.report = "ConsoleReport";  
Specs.run();

To run this, use the targeted version of the rake task:

:trunk:-> rake test:jsspec TEST=spec_string.js
(in /Users/jgehtland/RELEVANCE/project/trunk)
.
.

2 passed. 

Run 2 examples in 0.001seconds.

:trunk:-> 

(Simply running rake test:jsspec will run any file in your test/javascripts folder whose name begins with "spec" and report the results.)

We don't actually encourage you to write specs and tests for standard libraries like Prototype, JQuery, etc. It just makes for an easy demo.

If you want to test something that requires some HTML to bind against, I generally create a folder called test/javascripts/fixtures, and load HTML exemplars in there. For example, to test Prototype's $$ selector, you might create a fixture called fixtures/selector.html that contains the following:

<html>
<body>
  <div class="select_me"/>
  <span class="select_me"/>
  <div class="dont_select_me"/>
</body>
</html>

Your test, spec_selector.js, would now contain:

load("jsspec/config.js"); 
window.location = "fixtures/selector.html";
load("jsspec/jsspec.js");

with(Spec) {  
   describe("Prototype's $$ selector", function() { with(this) {  

     it("should find only elements with the provided class selector", function() {  
        ($$('.select_me').length).should(equal(2));
     });

     it("should find only elements with the provided tag", function() {
       ($$('div').length).should(equal(2));
     });

   }});
};

Specs.report = "ConsoleReport";  
Specs.run();

If you want these tests to be part of your regular CI process, just uncomment the second task in test_jsspec.rake and you can now have CI for your JS, ASAP.

Mar 06 2008

Comments

Frozen Gems Generator

Jay Fields blogged recently (and not for the first time) about managing gems within Rails projects. This is a problem a lot of people have wrestled with; there are close to a dozen plugins, rake tasks, uncommitted patches, and published hacks that attempt to provide a solution (and those are just the ones I know of).

FrozenGemsGenerator is the solution that we've been using on some projects at Relevance, and we're happy enough with it that we'll be using it more. It's a rails generator, packaged as a gem, that gives your Rails app a private gem repository, fully self-contained, and manageable just like your system-wide repository (except using script/gem instead of gem).

  $ sudo gem install frozen_gems_generator
  $ script/generate frozen_gems
  $ script/gem install money

script/gem supports all of the subcommands that the regular gem command does.

I haven't yet implemented a solution for gems that install binary extensions. I'm very interested in suggestions for how best to solve that problem. Several of the other approaches have at least partial support for architecture-specific gems; the best may be Jeremy Voorhis' CarryOn plugin, which is also the solution that's closest in spirit to the FrozenGems approach. If you have ideas or suggestions about how architecture-specific gems should be handled, please add comments here or post them on our Trac instance.

Apr 04 2005

Comments

Secret Service and Personalized Cracking

The Secret Service is into the decryption game (article) and its this kind of thing that makes me love computer security. Long story short, the SS has developed an app which scours the non-encrypted parts of a target's hard drive and creates a dictionary of terms from it that can be used to brute-force any encrypted portions. Even better, the software uses your browser bookmarks, follows them to their target sites, and uses those sites to expand the dictionary of terms. This gives them a focused corpus of words to attempt first, under the assumption that none of us is any good at picking or remembering passwords and we all use familiar material to help us.

I wonder if criminals use de.licio.us?

Apr 04 2005

Comments

Some Numbers at Last

So, a few weeks ago I made an offhanded post here about my new-found love for Rails. I'd been skipping off the surface of Ruby for a while, trying to decide if it, or Python, or Groovy, or something else, ought to fill out the empty slot in my tool belt. (I'll save the "why LISP isn't on this list" post for another time.) Rails seemed like an excellent way to put Ruby through a workout, and I had the right sized project to try it out with.

The project itself is not open-source; the client is now and shall remain anonymous. But they are paying me my going rate to do this work, which makes it a commercial Rails project, and it will in the future be installed into some rather large organizations. I can't really say much about what the application's domain is, but I can lay out the general principles of the app.

The application must support multiple users, in the order of 10-100 concurrently. The load isn't particularly high, but the application will be treated like a desktop app (more specifically, a spreadsheet replacement) so it has to be responsive. The application is for managing pieces of a large machine process. There are lots of types of components to be managed, and the relationships between them can be quite complicated. There are no one-to-one relationships in the model, but plenty of one-to-many and many-to-many. In addition to managing the components, the application has to allow for the addition of entirely new categories of components as well as a variety of customizable fields. Finally, the authorization rules call for a breadth of user roles with a complex mapping of permissions to those roles.

I've finally gotten around to running the profiling numbers and doing some comparison between the two systems. I won't spoil the suspense by listing my conclusions up front -- you'll have to scroll through the rest of the post to see them. But, first, let me set the stage: the original application is based on the Java/JSTL/Spring/Hibernate/MySQL stack. I used ACEGI for the security, SpringMVC for the web controller, and was using Hibernate 2.x for the O/RM. To increase performance, I was using the default output caching from SpringMVC and data caching in Hibernate. The re-implementation is in Rails on top of MySQL. The authorization is done through a custom observer and a custom authorization class, which uses a declarative rules file for permission mapping. For performance, I'm using a combination of page caching, action caching, and cache sweepers (observers whose job it is to expire cached pages).

Now, for the comparisons:

Time to Implement I made a comment about this in the previous posts on the topic, and that comment has been quoted widely out in the wide blogosphere as a classic example of Rails hype. So, let me make it plain: any time you re-write an application, it will go almost infinitely faster because you already have a firm grasp on the problem domain. Such was the case for me in my re-write; we'd spent so much time on the domain model and the database schema that the second time through the application, everything already made perfect sense to me. Any comparison of how long it took to implement one or the other is bogus, since the only fair comparison would be to implement two roughly functionally equivalent projects in the two different stacks and measure the competitive results. Since I have not done that, making statements about how it only took 5 days to re-implement the site are almost meaningless. What I can say is that I had more fun implementing it the second time, but that's just personal preference.

Lines of Code This one is a lot more interesting. Folks will tell you all the time that there is a running debate about the meaningfulness of LOC comparisons. They're right; there is a running debate. I just think it's moot. For my money, the fewer lines of code I have to write to get a piece of functionality, *as long as those lines are clear in meaning*, the better off I am. Obfuscated Perl programs don't make the grade; I can write some really concise Perl code and not have any idea, three months later, what the heck I was doing. But if the code is legible, its intent obvious, then more concise is better, pure and simple.

Bear in mind that this comparison is somewhat unfair. The Rails version of the app has been in development for an extra month (meaning a month's worth of new features added) while the Java version has been stagnant since the switch. Since the Rails version started out as an experiment, I don't have a clear history of the codebase to be able to produce a version that is identical in features to the Java version. Therefore, I've made the comparison based on the current state of the app vs. the abandoned Java version.

The LOC counts used here do not include comments or unit tests, nor do they include simple punctuation lines (lines with just a "}" or "end").

Without further ado:

  • Lines of Code
  • Rails: 1164
  • Java: 3293

Number of Classes:

  • Rails: 55
  • Java: 62


  • Number of Methods:
  • Rails: 126
  • Java: 549


  • Configuration Lines
  • Rails: 113
  • Java: 1161

Those numbers are beyond striking. The configuration count alone is enough to make me think long and hard about what I've been doing lately. Let me forestall any criticism about my "agenda", by the way. Somebody recently said that I must have an "anti-Java" or an "anti-Spring" agenda. That is far from the truth, since my Spring: A Developer's Notebook hits shelves any day now. I don't want people to stop using Spring, or Java. In fact, I want lots and lots of Java developers to use Spring and lots and lots of non-Java developers to start using Java so they can start using Spring. But one of the things I like most about Spring (its concise configuration) is still, well, huge, compared to the same app in Rails.

Performance This is where everybody really wants to see the numbers. So, for the sake of total specificity, the following numbers were generated on a 1.5GhZ Mac OSX (10.3.7) PowerBook with a 4200rpm hard drive and 1GB of RAM. The Java app is running on Jakarta Tomcat v 5.0.28, while the Rails app is running in Lighttp with FastCGI. The setups are standard for each application stack.

Since both stacks have different caching mechanisms, with different degrees of difficulty to manage them, I'll start the performance comparison with a walk through the application without using the caching systems. To generate the first number, I walked through every screen in the application (using the screens available in the Java version to form a subset of the screens available in the newer Rails version) hitting pages that have not been cached yet. This starts from logging in, then hitting every available piece of functionality once. These numbers do not include the time it took me (the user) to navigate to the next request, only the time to process the requests. Both applications had roughly equivalent logging turned on (obviously, it can't be exact, but without the logging, I couldn't provide measurements). Here's what I found out.

To walk the entire application feature set, once, in Rails, without caching, took 41.801s. To walk the exact same feature set in the Java app took 58.369s. These numbers are averages over five attempts with each app, with full restart between to give the cleanest runs possible. Those summary numbers are deceiving, though. What I found was that, the less complex the feature, the faster the Java app served it relative to the Rails app. The more complex the feature, the slower the Java app served it relative to the Rails app. Some of that difference might be changes to the model during the re-implementation based on a better understanding of the domain. Regardless, that difference comes out to show the Rails app at ~30% faster than the Java version when caching isn't taken into account. If I am generous and say that half of the difference is due to optimizations in the model, that still leaves us a 15% better performance in Rails.

The caching story is interesting. The default caching mechanism for Rails is either page caching (caching the output of the rendering to a physical html file and letting the server serve it directly instead of invoking Rails the next time it is requested) or action caching (similar, except the output is cached in memory or another kind of store and Rails is invoked to process the request). On the Java side, there is JSP pre-compilation, Spring output caching and of course Hibernate data caching. I used a simple load tester to test a couple of pieces of functionality in the application. For each URL, I ran the test without a pre-existing cache of the response, then again with it, 100 times each, and then determined the number of requests per second.

Functionality 1: Rails, 100 runs, no pre-existing cache: 75.59 request/second. Rails, 100 runs, pre-existing cache: 1754.39 requests/second. Java, 100 runs, no pre-existing cache: 71.89 requests/second. Java, 100 runs, pre-existing cache: 80.06 requests/second.

Functionality 2: Rails, 100 runs, no pre-existing cache: 62.50 r/s. Rails, 100 runs, pre-existing cache: 1785.15 r/s. Java, 100 runs, no pre-existing cache: 80.06 r/s. Java, 100 runs, pre-existing cache: 88.97 r/s.

These numbers are not a great comparison, because there is tons more I can do to increase the cache performance of the Java application. No doubt whatsoever (I just hadn't gotten around to it by the time I abandoned it). What *is* interesting, though, is that the Rails page caching maxes out . The server can't serve the pages any faster than that. And with the Rails cache_sweeper observer pattern, it is dead-simple to use this hyper-fast caching whenever possible (obviously, highly dynamic pages can't be cached, but that's the case with that kind of page in any application stack). If you switch to the action caching (which allows all the filters to be executed) the Rails app still ends up in the 800-1000 requests/second range.

Conclusions So what do I think? I think that the application I'm working on is perfectly suited for Rails and Rails is perfectly suited for it. I think that I have had more fun working on the Rails app than the Java version. However, I think that the Java version is just as capable, and could be just as performant, as the Rails app. To me, the eye-opening revelation isn't "Rails is faster than Java/Spring/Hibernate". It's "Rails can be very fast". I think that there is a lot to be said for Rails, and it deserves much of the press it is getting. However, I don't think its a Java-killer. I think there are plenty of applications, and development teams, that are better suited to Java and its immense universe of available support libraries. I certainly am not going to stop developing in and learning about Java just because I've discovered Rails. On the other hand, I am going to spend more of my time trying to find projects that I can use Rails on.

I'm extremely interested in how these numbers strike folks, and whether there are other comparisons that you would find useful. So interested, in fact, that I'm risking blog-spamming by turning comments back on. So, if you have some other comparisons you would find useful, or some insight onto these numbers, or even some things you think I ought to try to make the comparisons better, let me know. I can't promise I'll tackle them all, but I think it will be interesting.

Popular Tags