Five tips for testing Rails

Posted on May 04, 2008

Tip One – Debugging tests

I use RDebug in my apps a lot, but frequently I found myself wanting to debug my tests. Here’s how you can do it:

If you haven’t done so already, install RDebug
  $ gem install ruby-debug
Insert debugger in your code where you’d like to breakpoint
    def like_bananas?
      # Want to breakpoint here and see what the self.fruit attribute looks like
      debugger
      return self.fruit.likes.include?('bananas')
    end      
And then run the test from the command line using rdebug like this:
    rdebug test/unit/bananas_test.rb

You could even put the debugger reference in your test code as well.

Tip Two – Gently ease yourself into BDD with shoulda

Everybody’s talking about Behaviour Driven Development these days – and the popular BDD tool for Rails is the mighty RSpec.

But, it can seem like quite a bit of a jump to get into RSpec. Maybe you’ve got a well established application with a plethora of Test::Unit tests that you can’t bear to look at. Or maybe you just wanna try out the whole BDD approach for a while and see how it goes without really committing.

Well never fear – there’s a really easy way of easing yourself into BDD and it’s called shoulda. This is a great BDD tool made by the crazy freakin’ geniuses (genii?) over at thoughtbot.

Basically, shoulda is a BDD framework built on top of Test::Unit. It uses all the Test::Unit assertions that we know and love – adds some exotic delicous syntactic BDD sugar – and throws some wonderful helper macros in as freebies..

The thoughtbot folks rabbit on about it over here: http://thoughtbot.com/projects/shoulda.

An example test unit class might look like this:
class FruitBowlTests < Test::Unit::TestCase

  def setup
    @fruit_bowl = get_fruit_bowl
  end

  # Check out this BDD coolness
  should "be best before date" do
    assert @fruit_bowl.fresh?
  end

  # But you can still include old skool tests like this
  def test_fruit_bowl_has_three_fruit
    assert_equal 3, @fruit_bowl.fruit.size
  end

end

So you can try out BDD without messing up your current tests – hooray! Shoulda actually provides way more than this though – but don’t take my word for it, gem install shoulda today!

PS: Not only that, but the shoulda guys include a script that modifies your Test::Unit tests and changes them into shoulda style tests. Run it like this:
 $ ./vendor/plugins/shoulda/bin/convert_to_should_syntax [path to test file]

Tip Three – DRY up your tests with define method

Ever find yourself writing the same tests over and over again but with slightly different modifications – like this:
  def test_banana_should_be_nil_if_fruit_bowl_rotten
    @fruit_bowl = FruitBowl.new(:rotten => true)
    assert_nil @fruit_bowl.banana
  end

  def test_apple_should_be_nil_if_fruit_bowl_rotten
    @fruit_bowl = FruitBowl.new(:rotten => true)
    assert_nil @fruit_bowl.apple
  end

  def test_pear_should_be_nil_if_fruit_bowl_rotten
    @fruit_bowl = FruitBowl.new(:rotten => true)
    assert_nil @fruit_bowl.pear
  end

Using the wonderful Ruby methods define_method and send you can dry this up really easily like thus:

# Put the different bits into an array
[:banana, :apple, :pear].each do |fruit|
  # Iterate over the array and create a test method for each object.  Remember to prefix the method with 'test' when using Test::Unit
    define_method "test_#{fruit}_should_be_nil_if_fruit_bowl_rotten" do
      @fruit_bowl = FruitBowl.new(:rotten => true)
      # And use the send method to check the correct attribute
         assert_nil @fruit_bowl.send(fruit)
    end
  end

Tip Four – Test the objects behaviour

Here’s a tip that should revolutionise your testing methodology forever…

Make sure you test the correct behaviour for an object

What this means is that when testing an object, don’t create a test that looks outside of its scope.

For example:

Up until very recently I used to be a big fixtures man. It wasn’t uncommon for me to write functional tests like this:

    def test_transfers_heading_should_be_on_page
      get :show, :id => festivals(:festival_with_transfers_heading)    
      assert_select 'h3#heading', "Transfers" 
    end

Which was to test that the h3 with the id of ‘heading’ displayed the correct text, in this case “transfers”.

But deep down inside I was relying on this test to do more than what it should be doing. Whenever this test was run it had to load the festival from the database, and show the correct template. However, the scope of the test though was just to test that the correct information was shown in the view, anything else was irrelevant to this test.

A better test could look like this:
    def test_transfers_heading_should_be_on_page
      festival = Festival.new(:heading => 'Transfers')
      Festival.stubs(:find).returns(festival)
      get :show, :id => 1
      assert_select 'h3#heading', "Transfers" 
    end

The database doesn’t get hit because it doesn’t need to get hit. All we’re testing here is that the correct heading is displayed on the page – that the controller fulfilled its obligation by marshalling the data from the model to the view.

Tip Five – Expectations are tests too you know!

Mocks Rock – there I said it!

But, you can get so obsessed with stubbing out all manner of stuff for your assertions that you can mess out on all the nice inbuilt assertions built into mock objects – expectations.

For example, you might have an ActiveRecord observer that does some stuff after a model is saved like this:
  class FruitObserver < Observer

   def after_create(fruit)
     # Add to all the inventory lists
     InventoryList.find(:all).each do |i|
       i.stock.push fruit
     end    
   end
  end
You could use a mocha expectation to test this without using a single assert_blah like this:
  def test_when_creating_banana_observer_is_called
    banana = Fruit.new(:name => "banana")
    InventoryList.any_instance.expects(:push).with(banana)
    banana.save
  end

You don’t need to have assert_blah in all your tests – this is a perfectly acceptable test because it includes the expectation InventoryList.any_instance.expects(:push).with(banana).

Is your Mephisto Blog not accepting new comments?

Posted on April 18, 2008

This blog uses Mephisto – which is pretty cool and works a treat – but I realised to my horror the other day that the commenting system appeared to have stopped working.

It drove me bananas! Why would it work one day, but not the next! Why would it work on my local box but not on production?!?

Turns out there’s a known bug with Mephisto when used with Apache that prevents additional comments from being added to an article that already has a comment.

You can fix this by adding the following line to the bottom of your .htaccess file:

  DirectorySlash Off 

And voila – comments will start working again.

Thanks to Jason Gill for this tip.

Five tips for contributing to Rails

Posted on April 13, 2008
Note: these tips assume a basic understanding of how to contribute to Rails. If you’ve never contributed before I heartily recommend you check out Josh Sussers presentation Laying tracks or Ryan Bates excellent railscast on the topic.

Tip One – Test ActiveRecord tests with different database types

When creating a patch for ActiveRecord you need to make sure your patch works with different databases. I learned this the hard way when I submitted a patch that worked perfectly in mysql and broke in every other database type.

If you have the database management system set up correctly and also added the required databases (activerecord_unittest and activerecord_unittest2) then you can run the following rake task in the rails/activerecord folder:
  $ rake test_[database type]
Where [database type] is the database system you’re looking to run the tests on. For example:
  $ rake test_mysql
  $ rake test_sqlite
  $ rake test_postgresql
You can also use this rake task to run a specific ActiveRecord test. For example, to run the base_test.rb tests using mysql:
  $ rake test_mysql TEST=test/cases/base_test.rb

Tip Two – Run ActionPack tests in TextMate

When I’m working on a Rails application I get pretty addicted to running tests in TextMate with Command-R. I found when working on Rails patches that it wasn’t quite so tidy.

ActionPack tests typically have require ‘abstract_unit’ at the beginning of the file.

At the top of the test file add:
$: << ".." 

An explanation of what these strange hieroglyphs mean:

  • $: is the search path (you can think of it as being an array of directory path names)
  • ”..” the parent folder – you could have ’../tests/blahblah’ etc if you wanted to
  • << pushes an element onto an array

And now it finds ‘abstract_unit’ in the parent folder, allowing the tests to run within TextMate – hooray!

Obviously, if abstract_unit isn’t in the parent folder, then you should adjust this to search in the right folder for abstract_unit. For example:

$: << "../tests/helpers" 
Note: Make sure you remove the extra line before creating your patch

Tip Three – ActiveRecord script/console

Anyone who’s spent anytime working on an ActiveRecord Rails patch will realise that the included test models can get pretty complicated. That’s why I like to create a special IRB console for playing around with the relationships.

  • Still in the activerecord folder run:
    $ script/console
  • This opens a special IRB console that includes all the Rails test models and fixtures – allowing for cool stuff like this:

  • You can open it with different database types like this:
    $ script/console sqlite3
    $ script/console postgresql
    etc

Tip Four – Go through old unloved patches and fix them up

Stuck for ideas on how you can contribute to Rails? One great way is by going through old Rails patches in Trac and bring them up to speed.

Some patches work fine, but haven’t been accepted for a number of reasons:
  • They need documentation
  • They need tests
  • The code that the patch applies to has been changed

This link provides a list of patches that are untested, undocumented and incomplete.

Or you could just browse through the old open bugs here for patches that work but need some attention.

Tip Five – Validate other peoples patches

Last year, the Rails Core team made a decision that patches need to be verified by three other contributors before being considered for acceptance. With this arrangement, the Core team now no longer have to waste their time examining half-finished patches, and the quality of patches are higher.

Verifying a patch is more than just agreeing with the idea, it requires a bit of effort.

Here’s the steps you need to take to verify a patch:
  • Find a patch that needs verifying.

This bit’s easy. Just waltz on into #rails-contrib on freenode and ask “anyone want me to +1 their patch?” and prepare to be bombarded with requests.

  • Agree with what the patch does

This may seem obvious, but there’s no need to verify a patch if you don’t believe that the patch belongs in Rails.

  • Look at the source code

Give the code an eyeball in trac before downloading it. Make sure that the patch includes appropriate tests and documentation, and the code doesn’t have any glaring peculiarities. If it’s lacking in one of these areas post a comment on the ticket so that the contributor can make the necessary fixes.

If however you’re happy with how the patch looks then it’s time to take it for a test drive.

  • Update your own Rails repository

This is so that when you install the patch you can be sure that it patches cleanly. If it doesn’t then post a comment on the trac ticket letting the contributor know so they can fix it.

If you’re using subversion then run
    svn update
  • Download the patch

Sometimes trac tickets have multiple patch files, so make sure you’ve got the right one(s).

  • Install the patch

Now install the patch on your own repository using a command like:

    $ patch -p0 -i [patch_file.diff]
(Replace [patch_file.diff] with the file path of the patch you want to apply.) If the patch doesn’t apply cleanly you’ll get a message like:

    Hunk #2 FAILED at 738.
This is a sign that the code base has changed since the patch was created. The contributor will need to update the patch. Make sure you post a comment on the ticket with the details.
  • Run the tests to make sure they all pass
    $ rake test
  • Try out the patch with a test Rails application.

I like to keep my Rails repository in the vendor folder of a dummy application that for testing patches.

  • Verify

If the patch works, and there are no obvious side effects, then you can +1 the trac ticket. Simply post a comment on the ticket with something along the lines of:


    +1 tests pass

If you find that you’re the third person to verify a patch, then add the verified keyword. This signals to the core team that this patch is ready for examination.

  • Revert the patch
Run something like
    svn revert . -R

To ensure that the patch is no longer applied to your repository.

Where's my blasted gems?

Posted on March 21, 2008
If you’re anything like me, and you keep forgetting whereabouts your gems are installed, use the following command:

gem environment
This will pop up a handy list like this:

RubyGems Environment:
  - VERSION: 0.9.4 (0.9.4)
  - INSTALLATION DIRECTORY: /usr/local/lib/ruby/gems/1.8
  - GEM PATH:
     - /usr/local/lib/ruby/gems/1.8
  - REMOTE SOURCES:
     - http://gems.rubyforge.org

Your gems are nicely tucked away within your GEM PATH directory

Add a picture to an excel file using YAJB and railsxls

Posted on February 13, 2008

Ever find yourself wanting to generate an excel spreadsheet that includes logos or images?

If you want to generate xslx spreadsheets it’s not too bad, because you can embed the image binary in the markup – but for older excel formats it can get a little trickier.

Enter railsxls – an excel plugin for Rails!

railsxls is basically a wrapper around the funky POI library, so anything you can do with POI you can do with railsxls.

“But Chris, how can a Rails plugin use a Java library?!?”

Well I’m glad you asked – by using Yet Another Java Branch (or YAJB for short) This exciting little gem let’s you calls out to Java libraries using Ruby code.

Niiiice.

“Ok, so I can use the POI library commands to insert pictures onto an excel spreadsheet?”

Well…kinda…here’s how!

First install railsxls and yajb (details here)

The POI code that handles addPictures uses byte arrays, which YAJB has a hard time transporting between Java and Ruby. (Well, it did for me anyway)

So instead you have to use a jlambda, which let’s you run chunks of Java using ruby…like this:
  # Here's the java code I want to run
  java_code = "System.out.println(message);" 

  # And here's the jlambda that sets it up
  # Note the first param "String message" which let's you determine what param to pass in
  lambda_to_run = jlambda("String message", java_code)

  # Now we're actually CALLING the code and passing in a string
  lambda_to_run.call "bananas!" 

So, that’s a jlambda!

Now, with this new found knowledge, let’s call the appropriate POI code to create a picture. (I put the java code in the here_doc)

  # First off, let's use YAJB calls to import some java libraries
  jimport "java.io.*" 
  jimport "org.apache.poi.hssf.usermodel.*" 

  # Now create the new HSSFWorkbook option
  workbook = jnew :HSSFWorkbook

  java_code =<<JAVA
  java.io.File file = new java.io.File("#{File.join(RAILS_ROOT, 'tmp',"rails.png")}" );
  java.io.FileInputStream fis = new java.io.FileInputStream(file);

  byte[] bytes = new byte[(int) file.length()];
  fis.read(bytes);
  return new Integer(workbook.addPicture(bytes, org.apache.poi.hssf.usermodel.HSSFPicture.PICTURE_TYPE_PNG));
  JAVA

  # Create a worksheet in the workbook - an important step!
  sheet = workbook.createSheet("new sheet") 
  row = sheet.createRow(0)

  # This patriarch business is used for writing images and autoshapes
  patriarch = sheet.createDrawingPatriarch

  # Anchors are used for positioning
  anchor = jnew :HSSFClientAnchor, 0,0,0,255,2,2,4,7;
  anchor.setAnchorType( 2 );

  # Work your magic jlambda!  (Pass in the workbook as a parameter)
  p2 = jlambda("org.apache.poi.hssf.usermodel.HSSFWorkbook workbook", java_code)
  image_index = p2.call workbook

  # An image index is returned and this is used by the createPicture function
  patriarch.createPicture anchor, image_index
The code doesn’t cover saving the workbook, but you could do something like this:
  temp = Tempfile.new('railsworksheet-', File.join(RAILS_ROOT, 'tmp') )
  out = jnew :FileOutputStream, temp.path
  workbook.write(out)
  out.close
  File.open(temp.path, 'rb') { |file| file.read }

Now go erm…insert images!

assert_select the order of a drop-down in Ruby on Rails

Posted on January 14, 2008

To write a functional test to determine the correct order of a dropdown do the following:

    option = css_select("select#id_of_dropdown option")[n]
    assert_equal "Value To Compare", option['value'].to_s

And replace id_of_dropdown with the dom id for the drop down you’re looking for, replace n with the nth number, and replace ‘Value To Compare’ with the value you’re looking for.

So for example, if you have a dropdown with an id of ‘fruit’ and you want to make sure that the third option is ‘banana’ use this:
    option = css_select("select#fruit")[3]
    assert_equal "banana", option['value'].to_s

Ruby on Rails: Grabbing the model object using the controller name

Posted on January 09, 2008

I found myself in a situation where I was creating basically the same method in many of my controllers, the only difference being the name of the model object. For example:

class ArticlesController

 # .... cut 

 def set_recommended(is_recommended)
    @article.recommended = is_recommended
    @article.save    
    respond_to do |format|
      format.js { render :nothing => true }
    end    
 end

class MoviesController
 # .... cut 

 def set_recommended(is_recommended)
    @movie.recommended = is_recommended
    @movie.save    
    respond_to do |format|
      format.js { render :nothing => true }
    end    
 end

end

So, I want to refactor this code, and create one method for this in the application.rb file – but how to refer to the @movie and @article objects?

Simply by using the instance_variable_get method! You simply pass in a string with the name of the method, and it returns the instance variable.

Here’s how the new method looked within the application.rb file.
class ApplicationController
 # .... cut 

  def set_recommended(is_recommended)
    object = instance_variable_get("@#{self.controller_class_name.chomp("Controller").singularize.underscore}")
    object.recommended = is_recommended
    object.save    
    respond_to do |format|
      format.js { render :nothing => true }
    end        
  end

end

Of course, this only works for controllers and model names that follow the singular, plural convention (i.e. model name is article and controller name is ArticlesController)

Default columns in fixtures

Posted on January 03, 2008

Ever find yourself writing out the same old column names in fixtures just so your tests will work? Ever wonder if there’s a better way? Well there is!

Fixtures, now with NEW default columns!

All you need to do is bung a method in your test/test_helper.rb file that looks like this:

  def default_columns
    columns = <<END_OF_STRING
    created_at: 1 Jan 2007
    created_by: 1
    updated_at: 1 Jan 2007
    updated_by: 1
    lock_version: 1
  END_OF_STRING     
  end

Now, in each fixture that requires the default columns put this at the top:

<% require "#{RAILS_ROOT}/test/test_helper.rb" %>                    
And then for each fixture bung in this inline code:
one:
  id: 1   
  name: MyString
<%= default_columns %> 

Voila! No more repeating yourself with stupid columns! Hooray!

Note: spacing is important in yaml files – so make sure you put the <%= default_columns %> in an appropriate place

Quickly add Google Driving Directions to your rails application

Posted on August 27, 2007

Do you want driving directions on your website but don’t want to mess around with the Google Maps api?

Well, you can easily implement Google driving directions by calling the url: http://maps.google.com/maps?saddr=start_address&daddr=dest_address&hl=en

So, in Rails you could do something like this:
<%= button_to_function "Get Driving Directions",  "window.open(\"http://maps.google.com/maps?saddr=#{URI.escape(source_address)}&daddr=#{URI.escape(destination_address)}&hl=en\")" %>

Where source_address and destination_address are string variables containing things like:

London -> Manchester
SE5 0LL -> NW6 6RJ
221b Baker St -> 10 Downing Street

This method handles all the geocoding for ya – and is way simpler than messing with the GDirections api object.

How to dynamically serve a KML file with Ruby on Rails

Posted on August 24, 2007

Serving a KML file in Ruby on Rails is way easier than doing it in asp.net.

Here’s how I do it for my little display kml on a google map application.

First of all, you want to trick Google into thinking that the kml file is just a regular file with the extension “kml”. I do this by using a custom route in routes.rb that looks a little something like this:
map.connect ':controller/:action/:ignore_this_bit/:uuid.:format 
#The :ignore_this_bit is optional if you want to get around the google  
#caching issue detailed below
So that means you could serve the file with a url like this: http://www.my_domain.com/my_controller/get_kml/'random_number'/11.kml If you put a random number in there then you’ll get around Googles 4 minute cache. Next you create a controller method that will spit out the KML and render it to the browser using the send_data function like the example detailed below. (I load my kml data using a UUID by the way – but you can do it any way you want):
  def get_kml
    @kml_data = KmlData.find_by_uuid(params[:uuid])
    if @kml_data.kml     
        send_data @kml_data.kml
    else
      render :text => 'Kml is empty'
    end
  end

The @kml_data.kml variable contains the raw xml formatted kml.

The real guts of the function is the send_data function. This is rubys way of sending a file to the browser, which is how google expects to view the file.

And that’s it! Piece of cake!

Capture session expiration in Ruby on Rails

Posted on July 10, 2007

Dave Naffis blogged about this neat plugin that handles all kinds of session manipulation stuff, including the ability to call a method when you expire a session.

This was just what the doctor ordered for me – I had some cleanup code to call on session expiration, and darned if I could figure out how to do this…

With the SessionExpiration plugin it’s a piece of cake

class SomeController < ApplicationController
  expire_session_in 5.minutes, :after_expiration => session_expiry

  def session_expiry
    # Do awesome cleanup operations here!
  end

Daves blog: http://www.naffis.com

Install SessionExpiration like this:
script/plugin install http://svn.intridea.com/svn/public/session_expiration/

calendar_helper now highlights todays date

Posted on July 09, 2007

I contributed my first ever open source patch this weekend, to Geoffrey Grosenbachs awesomely simple calendar_helper Rails plugin.

You can install this into your current rails app with:
script/plugin install http://topfunky.net/svn/plugins/calendar_helper

My humble patch added the ability to display the current date on the calendar like so (the magic blue square):

Something fun for me was how amazing it was to create a patch using subversion – you just type
svn diff  > patch_file_name.diff

And it creates this wonderful magic patch file that details all the changes you’ve made! I just had to send this to Geoffrey and he did some subversion magic to easily commit my changes.

I think this creating patch thing is gonna get addictive…

ALSO I note that Tom Armitage added some functionality into this patch as well to do with what markup the calendar uses. (It was also his first patch experience). Find him here: Another day, another release – updated calendar_helper

Quick and dirty deny_select

Posted on June 24, 2007

I’ve just been learning how incredibly powerful Test First Development is in Rails – being able to test for elements on the dom using assert_select is fantastico!

However I note that there’s no opposite – a deny_select if you will. Something that will test to see if a given element does NOT exist.

Here’s one I cooked up earlier:


  def deny_select(element, message)
    assert_select element, false, message
  end

Use it by passing in the css element id and the message if the assertion fails like this:


  deny_select 'table.calendar', "Calendar should not be on page" 

Javascript conditionals in RJS

Posted on June 20, 2007

Something you can’t seem to do in RJS is to do conditional checking with the client side DOM.

For example, I want to be able to investigate an element to see if it has the CSS display tag set to none. The solution is to blend javascript and RJS together.

To do this you append the javascript as a string to the page object like this:


 # Javascript check to see if the element is visible
page << "if ($('div_element').style.display == 'none') {" 
  # Wow - this is like being in a PROPER if statement!
  # RJS stuff to make the element visible 
  page.replace_html 'div_element', :partial => 'some_partial' 
  page['div_element'].visual_effect :blind_down 
 # back to javascript 
page << '}' 

Thanks to Rails Casts for that particular little tidbit!

The Elements of Ruby Style

Posted on June 13, 2007
The Elements of Style is a fantastic little book detailing best practices for writing.

It's written as a series of rules about the best way of writing clear, concise elegant writing.

Subsequently, there's a series of programming language books that do a similar thing. One on my bookshelf is Baldwin, Gray and Misfeldts The Elements of C# Style that provides style guidelines for C#.

For example: Element 73: Choose Simplicity over Elegance
...there is nothing particularly elegant about a 50 line sequence of if-else statements, but if it's the simplest most straightforward approach to the problem, there is no reason to turn it into something it does not need to be.

And other tasty tid-bits.
If there's ever a language that's just CRYING out for a definitive style guide, it's Ruby. Rubyists are such purists about style, simplicity, DRY etc - but there doesn't seem to be (at least not to my eye) a list from on high as to best practices for style issues.

That was until I found Ian Macdonalds Unoffical Ruby Usage Guide that seems to be a pretty exhaustive study into Ruby best practices.
For example:
We recommend two blank lines between each class and module definition, and a single blank line between each method definition
Hooray!

oo - I also see Jeremy McAnally has created a project to do just this. Find it at: Elements of Ruby Style