Perfect solution for testing Rails applications (RSpec, Cucumber, Capybara, Selenium 2.0)

Testing is hard. Writing tests takes a lot of time, and running them can also be a pain in the ass. However, we have a few new tools in Rails toolbox, that can make it simplier and more fun. Much simplier and much more fun!

Let's start with Capybara

Capybara is a Webrat replacement. It does well what Webrat tried to do: gives you nice DSL for writing your Cucumber feature steps, without being tightnly bound to particular backend. With Capybara you can switch between Selenium and Culerity easily and painlessly. To install Cucumber with Capybara support, run the following from within your Rails project:

$ sudo gem install cucumber cucumber-rails
$ ./script/generate cucumber --capybara --rspec
$ RAILS_ENV=cucumber rake gems:install

the last step will install Capybara itself, with a few additional libraries. You can now write your tests using nice Capybara DSL, for example:

Given /^I am signed in as "([^\"]*)"$/ do |user|
visit path_to("the home page")
fill_in("Login", :with => user)
fill_in("Password", :with => "testtest")
click_button("Submit")
end

and switch backends with simple configuration change in features/support/env.rb:

Capybara.default_driver = :selenium # will use Selenium as testing backend
Capybara.default_driver = :culerity # switch to Culerity

In fact you can switch between backends for different tasks, so it's perfectly possible to test part of your application which is web service with Webrat, and rest with Selenium or Culerity.

it's worth mentioning that Capybara works by default with Selenium 2.0 (not Selenium RC), a.k.a WebDriver, and will work right out of the box with it, provided you have Firefox installed.

Clearing database with DatabaseCleaner

Nice addition that Capybara includes by default is use of DatabaseCleaner. This nice gem, will clear your database, and what is cool, it works with MongoMapper as well as ActiveRecord and DataMapper.

To use it with MongoMapper, add the following to your features/support/env.rb:

require 'database_cleaner'
require 'database_cleaner/cucumber'

DatabaseCleaner.orm = 'mongo_mapper'
DatabaseCleaner.strategy = :truncation

Getting rid of annoying Firefox window

Cucumber with Selenium RC used to show and hide 2 Firefox windows at every scenario. Now, Capybara creates one window and is re-using it for all the scenarios. This is already better, but if you don't want to be annoyed with Firefox popping up on your desktop at all, you can hide it, if by some chance, you're on Unix-like system.

First, you'll need a Xvfb server:

$ sudo apt-get install Xvfb # if you're on Ubuntu/Debian

Now, you can run your tests "headless":

$ nohup Xvfb -ac -screen scrn 1024x768x24 :2.0 &
$ export DISPLAY=:2.0
$ rake cucumber:all

Why is that so cool?

The setup described above works great with Ruby 1.8 and also with Ruby 1.9. Using WebDriver instead of Selenium RC brings nice performance improvements. DatabaseCleaner provides "transactional fixtures" to applications that use MongoMapper or databases that don't support transactions. Running your scenarios in Xvfb has the additional advantage, that you can do it on your server, integrate with CI server (say, Integrity) and do some cool stuff like creating a screenshot after every step... but let's leave that for next post!

Posted by Hubert Łępicki Sat, 09 Jan 2010 17:36:00 GMT


Amazon SimpleDB and Rails. BDD style.

Amazon SimpleDB is schema-free, non-relational database engine developed by Amazon and available as paid service as one of Amazon Web Services. It’s simple to use, fast and, combined with EC2 gives you great scalability out of the box.

One of biggest problems I had to face, when developing applications that use SimpleDB backend, is that I couldn’t run SimpleDB locally. No one can. It’s not available to purchase or download, you can’t do “apt-get install amazon-simpledb”. Using remote database for development is not a thing you want to do. It’s slow when used from outside of EC2 network. You probably need separate account for each of your developers. This sucks even more if you realize that you pay for running your tests… Hey, we want to run them as much as we can in BDD style – don’t want to be bothered by CPU usage it generates.

Solution to my troubles appeared to be M/Gateway’s M/DB. This is free and open source database, compatible with most recent (2009-04) API of SimpleDB, and you can run it on your own hardware. While it is possible to compile it from source, you probably need to pay developers for support on info how to do it ;). You won’t find also any packages for Ubuntu or Debian on the Internet (but if you do, please let me know!). The easiest way to get it up and running locally is to grab VMWare image and run it locally. Images and documentation how to set it up can be found on M/DB’s site. I run it on VirtualBox, which is my favourite among desktop virtualisation technologies, and have customized image a bit, but it’s equally easy to use it it VMWare. I’ll try to write detailed instructions on how to set up M/DB in VirtualBox some time next week on this blog. You also need to create 2 databases on your M/DB instance, and save access key id an secret access keys of those.

When you have M/DB installed locally, you need M/DB adapter for Ruby. Hey, didn’t I say it’s compatible with Amazon SimpleDB? So, existing adapters should work. Sort of.

The one I am using is Appoxy’s AWS combined with Appoxy’s SimpleRecord. First library is fork of abandoned RightScale’s AWS. I have made simple modifications to it, and now, you get M/DB support out of the box in master Git branch. You’ll need to manually build and install gem until nev version is released (which I show below). Second library is ActiveRecord pattern implemented on top of SimpleDB. Not as big, powerful and famous as Rails’ ActiveRecord, but good enough to provide object-oriented abstraction for your data for daily use.

To get started with these, install required gems:

# Until we have appoxy-aws version 1.11.36, we need to build gem manually:

$ git clone git://github.com/appoxy/aws.git
Initialized empty Git repository in /home/hubert/aws/.git/
remote: Counting objects: 377, done.
remote: Compressing objects: 100% (296/296), done.
remote: Total 377 (delta 198), reused 116 (delta 53)
Receiving objects: 100% (377/377), 167.74 KiB | 108 KiB/s, done.
Resolving deltas: 100% (198/198), done.
$ cd aws
$ gem build aws.gemspec 
WARNING:  no rubyforge_project specified
WARNING:  description and summary are identical
  Successfully built RubyGem
  Name: aws
  Version: 1.11.36
  File: aws-1.11.36.gem
$ sudo gem install aws
Successfully installed aws-1.11.36
1 gem installed
Installing ri documentation for aws-1.11.36...
Installing RDoc documentation for aws-1.11.36...

# in in future we'll just do:
# gem install appoxy-aws --source http://gems.github.com

# install simple-record
$ gem install appoxy-simple_record --source http://gems.github.com

Create your new M/DB Rails project:

$ rails mdb && cd mdb

Put these two lines in your config/environment.rb:

  config.gem "aws", :lib => "right_aws"
  # uncomment line below when version 1.11.36 is ready on github, and remove line above
  # config.gem "appoxy-aws", :lib => "right_aws", :source => "http://gems.github.com"
  config.gem "appoxy-simple_record", :lib => "simple_record", :source => "http://gems.github.com"

Now, you need to connect to your local M/DB instance when starting Rails. Edit your config/environment.rb to contain:

SimpleRecord.establish_connection(
  YOUR_KEY_ID_HERE,
  YOUR_SECRET_ACCESS_KEY_HERE,
  :mode => :per_thread,
  :server => IP_OF_YOUR_VIRTUAL_MACHINE,
  :protocol => "http",
  :port => 80,
  :signature_version => 2,
  :service => "/mdb/request.mgswi"
)

I advise you use app_config or AmberBit Config to use different keys for development and test environments, which will result in using different databases.

In order to check if your database is set up properly, let’s create a model “Brick”:

class Brick < SimpleRecord::Base
  has_attributes :weight, :color
end

There’s no migrations. To create table, for bricks, start up console and type:

$ ./script/console

Loading development environment (Rails 2.3.4)
>> Brick.create_domain
=> {:box_usage=>"0.0010000000", :request_id=>"eb4e3424-6371-89c4-330c-0c808d09ec52"}
>>

Hooray! Now, let’s see some more operations:

>> Brick.create(:weight => "1kg", :color => "red")
=> #, @attributes={"weight"=>["1kg"], "id"=>"1a9d944c-afaa-11de-9808-00a0d1a202b3", "color"=>["red"], "updated"=>[Sat, 03 Oct 2009 01:19:59 +0200], "created"=>[Sat, 03 Oct 2009 01:19:59 +0200]}, @new_record=false>
>> Brick.create(:weight => "0.5kg", :color => "brown")
=> #, @attributes={"weight"=>["0.5kg"], "id"=>"23111360-afaa-11de-9808-00a0d1a202b3", "color"=>["brown"], "updated"=>[Sat, 03 Oct 2009 01:20:13 +0200], "created"=>[Sat, 03 Oct 2009 01:20:13 +0200]}, @new_record=false>
>> Brick.find(:all).first.color
=> "red"
>> Brick.find(:all).last.weight
=> "0.5kg"

Cool, huh?

References

Posted by Hubert Łępicki Fri, 02 Oct 2009 23:24:00 GMT


Rails 2.3 - best release ever?

I have been using Ruby on Rails since the 1.1 release, and there have been more and less successful releases since. Rails 1.2 was amazing and brought us REST - most of us never looked back. However, the 1.2 series was still very buggy and was breaking backward compatibility several times between minor releases.

I wasn’t too much excited about Rails 2.0, particularly because it wasn’t backward-compatible and most of plugins had to wait for several months to be rewritten to work with 2.0. However, since then, Rails is evolving quite nicely and version 2.3 seems to be best you could ever get in Ruby web applications development. And here is why:

  • nested model mass-assignment significantly simplifies building complicated forms. At the time of writing, it’s still a bit buggy when it comes to validation (in RC1), but that is about to change before final release.

  • http digest authentication which is great news for everyone who build web services and need a bit more security in place.

  • metal, using which we can finally bypass routing monster and save some milliseconds for “mission-critical” requests.

  • overall performance boost, as pointed out by one of my friends. I didn’t see any benchmarks yet but overall feeling, especially with environment startup and running rake tasks is astonishing.

Now, let’s just wait for 3.0 revolution to come…

Posted by Hubert Łępicki Sun, 15 Mar 2009 21:25:00 GMT


Thread.new - dRuby now much more useful with Rails (2.2)

Rails 2.2 recently entered RC1 with loads of exciting features. Among built-in internationalization and bunch of other improvements, long awaited thread safety arrived. This was something that we (RoR users) were jealous when looking at Merb framework for quite a long time now.

Even if you are not using threaded server for your applications, and still use traditional mongrel/thin/whatever cluster, it’s good news for you. It’s even better if you use JRuby… but back to topic now.

Now, it’s easy to run long lasting tasks in your models for excellent description, look at Pratik Naik blog. It’s perfectly safe to write following code in your controllers now:

...

def create
  @post = Post.create(params[:post])
  Thread.new do
    @post.republish_content_on_some_slow_responding_sites
  end
end

...

However, I found thread safety even more useful for my recent project, with dRuby server running in the background. Now I can get rid of global mutex in my background process and enjoy one dRuby server shared among multiple server instances. I am running 5 instances of mongrel, each of them using heavily drb daemon which processes ActiveRecord objects - now I get 5x boost in speed with thread safe Rails :)

Posted by Hubert Łępicki Wed, 05 Nov 2008 22:48:00 GMT


Extending rails form helpers: a spinbox field

Spinboxes are common control in all desktop GUI applications. They’re found in all windowing toolkits that I know, and I use it all the time to input numbers. However, when it comes to web applications they are not so common. This is because (X)HTML doesn’t define such control.

I have found this great widget that requires prototype library only. It nicely degrades to text field when user doesn’t have javascript on, and is easy to use. However, I wanted to make it play nicely with Rails forms, to easily set up initial value etc.

Nothing easier. Just paste the following code to new file called lib/formhelperextensions.rb in you rails app:


module ActionView
  module Helpers
    module FormHelper
      def spinbox_field(object_name, method, options = {})
         min_val = options.delete(:min)
         max_val = options.delete(:max)
         tag = InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("text", options.merge({:class =>"spin-button"}))
         script = '<script type="text/javascript">new SpinButton($("'
         script += "#{object_name}_#{method}"
         script += '"),{'
         script += "min:#{min_val}" if min_val
         script += "," if min_val and max_val
         script += "max:#{max_val}" if max_val
         script += '});</script>'
         tag+script
      end

    end

    class FormBuilder
      def spinbox_field(method, options = {})
        @template.spinbox_field(@object_name, method, options)
      end
    end
  end
end

and include the file at the end of your config/environment.rb

....
require "form_helper_extensions"

Now, get a prototype-spin-button compressed archive, unpack it and copy spinbutton.js to your public/javascripts/ directory. Create file public/stylesheets/spinbutton.css and place following code in it:


input.spin-button {
  padding-right:20px;
  width:35px;
  background-color:white;
  background-repeat:no-repeat;
  background-position:100% 0%;
  background-image:url(/images/spinbtn_updn.gif);
}

input.spin-button.up {
  cursor:pointer;
  background-position:100% -18px;
}
input.spin-button.down {
  cursor:pointer;
  background-position:100% -36px;
}

Last bit is to copy image spinbtn_updn.gif from zip archive to your public/images directory.

And, when you include spinbutton CSS and spinbutton.js in your view, you can use spinbutton in your forms! Your layout should have:

  <%= javascript_include_tag :defaults %>
  <%= javascript_include_tag "spinbutton" %>
  <%= stylesheet_link_tag "spinbutton" %>

Use it in your forms, like you use text button:

 form_for @user....
   f.text_field :name
   f.spinbox_field :age, :min => 0, :max => 120
 end

Enjoy!

Posted by Hubert Łępicki Thu, 07 Aug 2008 07:53:00 GMT


BGUL / JTeam meetings - 02.04.2008 and 09.04.2008 ][

Posted by Hubert Łępicki Wed, 09 Apr 2008 21:33:00 GMT


JTeam/Bgul Example rails 2.0 application

Posted by Hubert Łępicki Thu, 03 Apr 2008 21:36:00 GMT


BGUL / JTeam meetings - 02.04.2008 and 09.04.2008

This is announcement for our local Linux/Java community at Bialystok Technical University but… I’m a speaker at next two meetings of BGUL, together with JTeam. Subject - JRuby on Rails - I’ll try to prepare some cool examples of code that you might expect to appear on this blog this weekend / next week even before the first meeting.

Exactly when and where? This will appear on JTeam and/or BGUL web sites.

Everyone is invited!

Posted by Hubert Łępicki Fri, 28 Mar 2008 22:34:00 GMT


TinyMCE and AJAX

TinyMCE is a visual XHTML editor that you can put on your web app, to convert textareas into Word-like editors. This way you can make it possible for your clients edit pages more easily, saving you lots of trouble explaining them basics ofHTML or even easier formatting languages. TinyMCE doesn’t play well with AJAX calls, however it’s easy to fix it. I will use TinyMCE with Ruby on Rails, version 2.0.2

Let’s start with creating new application:

                                                                     
     [hubert@swarog ~]$ rails tiny_mce_ajax                                     
       create                                                                   
       create  app/controllers                                                  
       create  app/helpers                                                      
       ...........................                                              
       create  log/development.log                                              
      create  log/test.log                                                      
     [hubert@swarog ~]$ cd tiny_mce_ajax/                                       
     [hubert@swarog tiny_mce_ajax]$ ls                                          
     README    app     db   lib  public  test  vendor                           
     Rakefile  config  doc  log  script  tmp                                    

Now, let’s install tiny_mce plugin:

                        
     [hubert@swarog tiny_mce_ajax]$ ruby script/plugin install http://secure.near-time.com/svn/plugins/trunk/tiny_mce                                           
     [hubert@swarog tiny_mce_ajax]$ rake tiny_mce:scripts:install               

TinyMCE plugin used to work right after you do it, however something has changed either in Rails or the editor plugin that requires us to make a little hack here:

First, open this file with your favorite editor:

                                     
     vendor/plugins/tiny_mce/lib/tiny_mce.rb    

and put these two lines at top of the file:

                    
     require 'tiny_mce_helper'             
     include TinyMCEHelper                 

We’re ready to go. First, let’s create a RESTful controller:

                                                 
     [hubert@swarog tiny_mce_ajax]$  ./script/generate scaffold note body:text
     [hubert@swarog tiny_mce_ajax]$  rake db:migrate                          

An add TinyMCE and Prototype script links to HEAD section of our layout file:

                                                                  
                                                                                
     <%= tiny_mce %>                                                            
     <%= javascript_include_tag :defaults %>                                    

At this point, we have TinyMCE installed in our application, that will be used on all textareas. However, this will not work if we add another textarea using JavaScript and/or AJAX calls.

To make it work, let’s add partial to our application:

file: app/views/notes/_new.html.erb

                        
     <% form_remote_for :note, @note, :update => { :success => "notes" }, :position => :bottom,     :url => { :controller => "notes", :action => "create" }, :after => "$('new_note').innerHTML = ''; "      do |f| %>                          
             <%= f.text_area 'body', :colls => 100, :rows => 10 %>
<%= submit_tag "Save", :onclick=>"javascript: tinyMCE.triggerSave(); tinyMCE.execCommand('mceRemoveControl', false, 'note_body'); " %> <% end %>