Deploying Rails Applications on Leopard: Part III

(Fri Mar 28, 2008) [/Rails#

Part III of my three-part Apple Developer Connection series on Rails has been posted. It's titled Deploying Rails Applications on Mac OS X Leopard because it picks up where we left off in Part II by deploying the application to Leopard Server using Capistrano.

I had a lot of fun writing this piece in particular because I got to play around with the new features in Leopard Server. It comes pre-installed with lots of Rails deployment goodies to streamline the process: Apache 2.2, mod_proxy_balancer, MySQL, Mongrel, Capistrano, and so on. As well, Apple created an enhanced version of mongrel_rails that, among other things, registers your Mongrel processes with Bonjour. The upshot is you can configure your Rails-powered web site right from Server Admin.

Enjoy!

Developing Rails Applications on Leopard: Part II

(Thu Mar 20, 2008) [/Rails#

Part II of my three-part Apple Developer Connection series on Rails has been posted. It's titled Customizing Rails Applications on Mac OS X Leopard because it picks up where we left off in Part I by working with views and web forms, adding AJAX support, and supporting an iPhone interface.

Coincidentally, we're offering another back-to-back Studio combo in June:

Register by April 18th and save $300 on each Studio.

Enjoy!

Developing Rails Applications on Leopard: Part I

(Tue Mar 04, 2008) [/Rails#

My latest Apple Developer Connection article, titled Developing Rails Applications on Mac OS X Leopard, is the first in a three-part series on Rails. It takes you through how to build a basic RESTful app using Rails 2.0.2 and XCode.

Parts II and III go beyond the basics to customize the application and deploy it on Leopard Server. They've already been written, and they should be available soon.

This series has been a long time in the making, and I'm delighted that it's finally available. It's a major update to the Tiger version of the article. Leopard comes with a bunch of pre-installed goodies to help you start building (and deploying!) Rails applications without a lot of fuss. And although I still use TextMate, it's great to see Rails support making its way into XCode 3.0. Even if you're a Rails pro, you may want to follow this series to see what Leopard brings to the party.

Testing Rails Controllers: Follow-Up

(Sun Oct 07, 2007) [/Rails#

Herewith, responses to my survey How Would You Test This? regarding tools and techniques folks are using to test Rails controllers, and my humble thoughts at the end.

Mike Mangino shows you how he'd write an RSpec spec for my controller, and points out the style they use on projects.

"Our goal with controller tests is to create very simple, easy to read tests that cover the entire controller. We believe strongly in one assertion per test, so we will have a larger number of small tests. Our typical pattern is to stub everything at the beginning, and then mock as necessary to validate behavior."

Pat Maddox has found "testing controllers is generally child's play, and the model is where I run into the most trouble." He offers a step-by-step tutorial for BDDing my controller from start to finish using RSpec, and concludes with

"My controllers are easy to spec because they should be easy to spec. They just take some objects and tell them to do stuff. It's what those objects do that is complex. You can use mock objects to isolate that complexity. If you don't use mock objects, then you end up with leaky abstractions that make your tests harder to understand and maintain."

Jay Fields has never been able to accept that controllers can only be functionally tested. He looks forward to the day when we can instantiate controllers anew and unit test them:

"I think part of the problem is that controllers are not Good Citizens. Controllers violate the first rule of Good Citizenship. Upon creation (Controller.new), controllers are not in a valid state. Instead, controllers depend on being initialized within the framework and having their state set post construction time. That ends up being a problem for unit testing since it requires each test to set additional state on newly created controllers."

James Herdman wrote in to say he's a "hold the onions" sort of guy who prefers real objects over mocks/stubs whenever possible.

"Avoiding the database would be nice, but sometimes it's good to hit up your models in controller testing. Sometimes the angle at which we hit our models in the controller isn't quite as we intended when writing our models, or sometimes you expose bad behavior you didn't quite realize could happen... I play by this rule of thumb when it comes to mocking and stubbing with controllers: Fake out stuff that doesn't really matter, and fake out what's directly out of you control (e.g. external resources). (The drawback is, of course, that my tests take bloody ages to run through. But I'd rather stuff go afoul on my system than in production.)"

Kevin Barnes gave a nod to the shoulda plugin and finds the nested contexts to be handy on his "large-scale Rails app (EMR w/ 120+ models)".

Jim Morris uses RSpec with mocks, and pointed out a technique he came up with to automatically test all actions within a controller using RSpec.

I appreciate the time these folks took to write up examples and opinions. Hearing how other Rails developers are going about testing controllers gave me new perspectives, and that always helps me. I hope you picked up a new idea or two along the way, or maybe even found more confidence in the approach you're using. Indeed, perhaps you realized that you're already doing what the other cool kids are doing. At the same time, I appreciate that seeing varying tools may inadvertently cause some paralysis among those folks trying to get started.

If all this left you wondering how to get started, I offer you this embarrassingly obvious, but painfully honest, experience report: Testing is 90% discipline. The hard part is actually sitting down and doing it on a regular basis, where regular basis means "as you're writing code". Whether it's before or after you write the code, with Test::Unit, RSpec, test/spec, or tomorrow's testing framework, using mocks, stubs, or neither of the above is an implementation detail. Now, the tools and techniques (the other 10%) can help you keep the discipline. So choose tools that you're comfortable using on a regular basis, regardless of what's cool, and stick with them. And then focus the energy you might otherwise be spending on the worry of whether you're doing the right thing on the thing that matters most: Making great software.

How Would You Test This?

(Sat Sep 08, 2007) [/Rails#

It's not a trick question, and there is no right or wrong answer. The state of the art of Rails testing has evolved since the early days. Keeping up has proven both rewarding and at the same time a bit of a struggle. I'm continually refining the way I write tests to help break writing the code down into small, safe steps and defer decisions with confidence. And I'm generally curious to see how you're testing controllers these days.

Why controllers? Simply because they feel more challenging to test than models. And folks who are perfectly fine with test-driven development at the model level are often overcome with reluctance and frustration when they start coding controllers. The hesitation has an upside: It prompts one to move business logic into models, where the logic belongs and where it's easy to test.

However, the frustration has a downside. Having fattened up the models, it's easy to then throw in the towel when it comes to testing controllers. But given that we're writing web applications—and controllers are central to how the world accesses our models—it seems fairly important to test controllers. That means being able to easily flesh out what can happen at a specific place: Given a particular state, if you poke a controller action in a particular way, what happens to the state (model objects, the session, visible display, etc.)?

Assume, for example, that you have the following simple create action in a controller, or assume you want to use TDD to force the writing of a similar create action:

class MenuItemsController < ApplicationController
  
  def create
    @menu_item = MenuItem.new(params[:menu_item])
    if @menu_item.save
      flash[:notice] = 'MenuItem was successfully created.'
      redirect_to menu_items_url
    else
      render :action => :new
    end
  end
  
  def new
    @menu_item = MenuItem.new
  end
  
end

Nothing exciting going on here. It's basically the same create action you've been writing since Rails 0.10.0. (I've removed the use of respond_to in this example just to keep it as simple as possible.) Testing this action isn't especially difficult, but as an example it allows us to focus on style. And in this brave new REST world where most controllers will have a create action similar to the one above, style matters. If the mechanics of writing a test get in the way of describing behavior we aim to implement, they'll we'll hate writing tests in exactly the way programmers are supposed to hate testing.

So, how would you go about validating the behavior of the create action? I'm hoping that by soliciting solutions we can all learn something. To help get things started, I offer the following styles that I've observed across a number of projects.

The Classic Cheeseburger

Your basic, get 'er done functional test topped with an assert_difference custom assertion (the cheese) that checks whether a new menu item was created in the database.

class MenuItemsControllerTest < Test::Unit::TestCase
  
  def setup
    @controller = MenuItemsController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
  end

  def test_create_with_valid_menu_item
    assert_difference(MenuItem, :count, 1) do
      post :create, :menu_item => {:name => 'Classic', 
                                   :price => 4.99}
    end
      
    assert_not_nil assigns(:menu_item)
    assert_not_nil flash[:notice]
    assert_redirected_to menu_items_url
  end
  
  def test_create_with_invalid_menu_item
    assert_difference(MenuItem, :count, 0) do
      post :create, :menu_item => { }
    end
  
    assert_not_nil assigns(:menu_item)
    assert_nil flash[:notice]
    assert_response :success
    assert_template 'new'
  end

end
Hold The Onions

This is the classic cheeseburger, marinated with FlexMock to remove the bitter aftertaste of the database. Tastes best with a healthy side dish of unit tests that check model validations, so the functional test doesn't have to.

require 'flexmock/test_unit'    

class MenuItemsControllerTest < Test::Unit::TestCase
  
  def setup
    @controller = MenuItemsController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
    
    @attributes = { 'name' => 'No Onions', 'price' => 4.99 }
    @menu_item = flexmock(MenuItem.new(@attributes))
    flexmock(MenuItem).should_receive(:new).with(@attributes).once.
      and_return(@menu_item)
  end
  
  def test_create_with_valid_menu_item
    @menu_item.should_receive(:save).with().once.and_return(true)

    post :create, :menu_item => @attributes

    assert_equal @menu_item, assigns(:menu_item)
    assert_not_nil flash[:notice]
    assert_redirected_to menu_items_url
  end

  def test_create_with_invalid_menu_item
    @menu_item.should_receive(:save).with().once.and_return(false)

    post :create, :menu_item => @attributes

    assert_equal @menu_item, assigns(:menu_item)
    assert_nil flash[:notice]
    assert_response :success
    assert_template 'new'
  end

end
The Daily Spec-ial

A light, sugary topping of test/spec on an old favorite, with a distinct flavor of BDD.

require 'test/spec'   
require 'test/spec/rails' 
require 'flexmock/test_unit'    

context 'Creating a new menu item' do
  
  use_controller MenuItemsController

  setup do
    @attributes = { 'name' => 'Daily Special', 'price' => 4.99 }
    @menu_item = flexmock(MenuItem.new(@attributes))
    flexmock(MenuItem).should_receive(:new).with(@attributes).once.
      and_return(@menu_item)
  end
  
  specify 'should redirect to index with a notice on successful save' do
    @menu_item.should_receive(:save).with().once.and_return(true)

    post :create, :menu_item => @attributes

    assigns(:menu_item).should.equal @menu_item
    flash[:notice].should.not.be.nil    
    should.redirect_to menu_items_url
  end
  
  specify 'should re-render new template on failed save' do
    @menu_item.should_receive(:save).with().once.and_return(false)

    post :create, :menu_item => @attributes

    assigns(:menu_item).should.equal @menu_item
    flash[:notice].should.be.nil
    status.should.be :success
    template.should.be 'new'
  end
  
end
The Whole Enchilada

This ain't no burger. It's an entirely different kind of (test-lingo-free) taste, smothered in a spicy RSpec syntax and served with a side of refried mocking.

require File.dirname(__FILE__) + '/../spec_helper'

describe MenuItemsController, 'Creating a new menu item' do

  before do
    @attributes = {'name' => "Enchilada", 'price' => 4.99}
    @menu_item = mock_model(MenuItem)
    MenuItem.should_receive(:new).with(@attributes).once.
      and_return(@menu_item)
  end
  
  it 'should redirect to index with a notice on successful save' do
    @menu_item.should_receive(:save).with().once.and_return(true)
    
    post :create, :menu_item => @attributes
    
    assigns[:menu_item].should be(@menu_item)
    flash[:notice].should_not be(nil)    
    response.should redirect_to(menu_items_url)
  end

  it 'should re-render new template on failed save' do
    @menu_item.should_receive(:save).with().once.and_return(false)

    post :create, :menu_item => @attributes

    assigns[:menu_item].should be(@menu_item)
    flash[:notice].should be(nil)    
    response.should be_success
    response.should render_template('new')
  end
  
end

This menu of controller testing styles is far from comprehensive, and it's not intended to be a subjective comparison. I specifically tried to introduce subtle variations along the way, while preserving the initial goal of testing the create action. Each style has its pros and cons. Perhaps just seeing a few different styles will trigger new ideas.

Now it's your turn to blog up a solution. Which tools and techniques are working for you? Send me links and I'll write a follow-up post summarizing the results. As well, I'm looking forward to seeing how Jim and Joe solve this in their Test-Driven Development with Rails Studio next month. I'd enjoy the opportunity to chat with you there.