The Ruby/Rails Life – My Rails 3 Stack – Part 2


Rails Deployment & Testing

There are some really nice tools available for deployment and testing rails. Below is a brief description

of some of the ones I’ve been using with success.

Capistrano

I remember in the early days of my attempts of using Rails back in the 1.x days, the deployment/hosting

story was a bit of a headache, even with Capistrano back then. However things have improved dramatically,

especially with things like Phusion Passenger. Capistrano is still strong too and I’ve

found that it makes deploying Rails apps extremely simple. The ability to make that “one quick change” and redeploy

in seconds is an awesome feeling.

Here is a somewhat annotated example of what my deploy script looks like just to give you a glimpse of what a

Capistrano script looks like (with certain information removed of course). If you’ve never used Capistrano before

then this might look a bit foreign. Again, this is just a taste, but you’ll have to dig in a bit more to really

understand the moving parts here.

Example

/config/deploy.rb

# just some basic info, including where to grab the code from
    set :application, "My Groovy Rails App"
    set :repository,  "[git repo]"
    set :scm, :git

    # there are some SSH options that i don't fully understand yet... :)
    default_run_options[:pty] = true
    ssh_options[:forward_agent] = true

    # ex. /var/www/my_groovy_rails_app
    set :deploy_to, "[path]" 

    # just some basic server properties
    role :web, "[server]"                   # Your HTTP server, Apache/etc
    role :app, "[server]"                   # This may be the same as your `Web` server
    role :db,  "[server]", :primary => true # This is where Rails migrations will run

    # configure environment variables to propertly use RVM (Ruby Version Manager) on the server
    # as you can see i'm using Ruby 1.8.7 Enterprise Edition on the server
    set :default_environment, {
      'PATH' => '/home/you/.rvm/gems/ree-1.8.7-2010.01/bin:/home/you/.rvm/gems/ree-1.8.7-2010.01@global/bin:/home/you/.rvm/rubies/ree-1.8.7-2010.01/bin:/home/you/.rvm/bin:$PATH',
      'RUBY_VERSION' => 'ree-1.8.7-2010.01',
      'GEM_HOME'     => '/home/you/.rvm/gems/ree-1.8.7-2010.01',
      'GEM_PATH'     => '/home/you/.rvm/gems/ree-1.8.7-2010.01:/home/you/.rvm/gems/ree-1.8.7-2010.01@global',
      'BUNDLE_PATH'  => '/home/you/.rvm/gems/ree-1.8.7-2010.01'  # If you are using bundler.
    }

    # automatically restarts the app after deployment
    namespace :deploy do
      task :start do ; end
      task :stop do ; end
      task :restart, :roles => :app, :except => { :no_release => true } do
        run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
      end
    end

    # tasks for bundler, which is the new way dependencies are managed in Rails 3
    namespace :bundler do
      task :create_symlink, :roles => :app do
        shared_dir = File.join(shared_path, 'bundle')
        release_dir = File.join(current_release, '.bundle')
        run("mkdir -p #{shared_dir} && ln -s #{shared_dir} #{release_dir}")
      end

      task :bundle_new_release, :roles => :app do
        bundler.create_symlink
        run "cd #{release_path} && bundle install --without test"
      end

      task :lock, :roles => :app do
        run "cd #{current_release} && bundle lock;"
      end

      task :unlock, :roles => :app do
        run "cd #{current_release} && bundle unlock;"
      end
    end

    # the actual task that runs bundler to ensure all dependencies on the server are up to date
    after "deploy:update_code" do
      bundler.bundle_new_release
    end

RSpec 2

A lot of you have probably at least heard of RSpec, with some of you perhaps even using it now. It’s a pretty

awesome testing framework, dare I say, BDD framework. Surprisingly I’ve really only scratched the surface as to

what RSpec is capable of, but it’s a joy to use for sure. So far I’m mainly using it for testing my models and a

little bit of controller testing. However I’m leaning on my higher level Cucumber integration tests for exercising

my controllers for the most part. I also haven’t gotten into mocking all that much yet, which RSpec has support for

as well. Perhaps at some point I’ll get into mocking. This is probably going to get me flamed by the ALT.NET’ers,

but honestly I just haven’t felt the pain in the fact that most of my tests DO in fact hit my local test database.

Perhaps that’s because I’m using MongoDB as my database which is so fast I don’t really notice that much of a

performance hit.

Example

Here’s a brief example of a simple model spec in RSpec.

/spec/models/user_spec.rb

describe User do
  context "first name" do
    it "is required" do
      user = User.new
      user.should_not be_valid
      user.errors[:first_name].first.should == "can't be blank"
    end
  end

  context "email" do
    it "is unique" do
      user1 = User.create!(:email => "abc@123.com")
      user2 = User.new(:email => "abc@123.com")

      user2.should_not be_valid
      user2.errors[:email].first.should == "is already taken"
    end
  end
end

Certainly not an exhaustive example, but it gives you the idea of how a basic spec looks in RSpec. And even this is

not nearly as elegant as I’ve been seeing others show lately. I would highly recommend you watch

@l4rk‘s presentation at the Scottish Ruby Conference on

Pure RSpec. I learned a ton from this presentation

and hope to incorporate some of his tips into my specs soon. Specifically the elegance of “let” and “subject”.

Oh, one more note. I’m using RSpec 2, which is still in the early stages, but works with Rails 3. And I’m

actually running it straight from the master git repo. So, use at your own risk!

Cucumber

I’ve named this cute little guy Larry. If you have kids, then you know why. 🙂 This is a pretty nice way of doing

high level acceptance/integration testing. It’s also the first time I’ve ever thought that ATDD (acceptance test driven

development) is actually achievable. The idea is that you write your “stories” in plain english, preferrably

with your client/customer/product owner using the give/when/then style of syntax. Once written, these stories can be

run through the Cucumber framework where it is parsed and coupled with custom ruby code (some of which you have to

write) to essentially give you executable specifications. Of all the attempts I’ve seen out there to achieve ATDD and

executable specifications, Cucumber really seems to have it nailed the best so far. Writing the stories does take some

getting used to as you do somewhat have to learn to speak the “language” of Cucumber. But after writing your first

couple “features”, as Cucumber calls them, you pretty much get the hang of it.

Example

/features/users/add_new_user.feature

Feature: Add new user
    In order to allow a new user to access the system
    As an admin
    I want to add a new user to the system

    Scenario: New link is clicked from the users page
        Given I am logged in as an admin
        And I am on the users page
        When I follow "New"
        Then I should be on the new user page

    Scenario Outline: Required text fields are blank 
        Given I am logged in as an admin
        And I am on the new user page
        When I fill in "" for ""
        And I press "Save"
        Then I should see an error for ""

        Examples:
            | field_name |
            | First name |
            | Last name  |
            | Email      |
            | Password   |

I have another post written that gives a bit more of an overview of Cucumber with more code examples, so look for that

soon.

Steak

I’ve only started to play around with this one, but it’s a pretty interesting take on acceptance/integration testing.

Unlike Cucumber, it doesn’t use plain text stories. Instead it’s basically just a small wrapper of aliases on top

of RSpec that give you same kinda feel as Cucumber, but without the overhead of the English language. I’m going to

be spiking a bit more with this one as I think it might come in handy for some of my other integration testing needs.

Oh, and gotta love its logo! Beef! It’s what’s for…testing?

factory_girl

Interesting name, very useful tool. You might have guessed that this is a tool to create objects for you. Not a whole

lot to say, but it’s really handy to easily create objects in varying states for testing purposes.

Example

/spec/factories.rb

# creates a new User model
  Factory.define :user do |u|
    u.first_name 'Bud'
    u.last_name 'Abbott'
    u.email { |x| "#{x.first_name.downcase}.#{x.last_name.downcase}@blah.com" }
    u.password 'secret'
    u.password_confirmation 'secret'
    u.group_ids { [Factory.create(:group, :name => 'Users').id] }
    u.roles ['manager']
  end

You can use this factory in a variety of ways.

# instantiates the User model only, does NOT save it to the database
  Factory.build(:user) 

  # creates the User model and saves it to the database
  Factory.create(:user) 

  # returns a hash of attributes representing this user
  # useful for testing controllers simulating params or for validation
  Factory.attributes_for(:user) 

  # I haven't really found a use for this one yet
  Factory.stub(:user)

  # You can override specific values if desired
  # This would build the user setting a name
  # of "Lou Costello" instead of the default "Bud Abbott"
  Factory.build(:user, :first_name => "Lou", :last_name => "Costello")

rstakeout

This is a nice little script I got from Geoffrey Grosenbach, of PeepCode fame. I use to automatically run my Cucumber

features and RSpec tests anytime I change a file in my Rails app. I find rstakeout to be a lot simpler to get going than

Autotest/Autospec. It has growl integration too. And yes, I do like my green and red growl notifications. 🙂

Example (from a terminal)

rstakeout "cucumber -t @wip"

If I run that in my terminal, it automatically watches all files in my Rails app for changes and runs the command I give

it on each change. In this case, it runs my cucumber features, but only ones I’ve tagged with @wip, meaning ones I’m working

on at the given moment.

I’ve posted a Gist of the rstakeout script I’m using since there has been some confusion over which version to use.

Conclusion

Well I hope that gives you a taste of some of the deployment and testing tools I’m using so far in Rails. I feel like I

still have a ton to learn of course, but so far I’m really enjoying the testing experience that the Ruby language gives

me.

What are your favorite deployment/testing tools in Ruby/Rails? Leave a comment and let me know!

The Ruby/Rails Life – My Rails 3 Stack – Part 1