Archive for the ‘Testing’ Category

Route Testing with Shoulda

January 27, 2010

I received a comment in a recent article, by someone who saw the default routes in my config/routes.rb example and suggested I remove them. Yes! This makes total sense from several angles.

Between RESTful routes and named routes, there’s no good reason not to explicitly name all of your routes. Also, you don’t get the goodness of say, “pets_path” or “pet_path(@pet)” if you just let the default routes handle PetsController for you. Finally, if you’re doing TDD (test-driven development) then why aren’t you testing your routes as well?

Route testing is easy, and the tests themselves run super fast. One app of mine has almost 600 routing assertions that run in just 2 seconds. It’s fun to watch the dots for your routing tests zoom across the screen! Let’s start by adding a test file to our system with the standard boilerplate:

# test/unit/routing_test.rb
require 'test_helper'

class RoutingTest < ActionController::TestCase

end

We’ll start with some TestUnit basics before we get into how Shoulda can make life easier. There are three assertions to learn:

def test_generates_user_index
  assert_generates '/users', :controller => 'users', :action => 'index'
end

def test_recognizes_user_index
  assert_recognizes {:controller => 'users', :action => 'index'}, '/users'
end

def test_routes_user_index
  assert_routing '/users', :controller => 'users', :action => 'index'
end

The first assertion, assert_generates, verifies that if url generators like url_for are passed this hash of controllers, actions and whatever else, the correct route (/users in this case) is generated. The next assertion, assert_recognizes, does just the opposite, ensuring that a route of /users end up calling the index action of the users controller. The third version (assert_routing) does both! It combines the two previous assertions into one, and this is what you’ll want most of the time.

Using Shoulda to DRY up your tests

Here are the above tests, in Shoulda form:

require 'test_helper'

class RoutingTest < ActiveSupport::TestCase
  context "routing users" do
    should "generate /users" do
      assert_generates '/users', :controller => 'users', :action => 'index'
    end

    should "recognize users" do
      assert_recognizes {:controller => 'users', :action => 'index'}, '/users'
    end
  
    should "generate and recognize users" do
      assert_routing '/users', :controller => 'users', :action => 'index'
    end
  end
end

While these tests are simple, they’re not very DRY. Just imagine, you’ll need to have seven of these tests *just* for the most basic RESTful route. Instead, install my shoulda routing macros:

script/plugin install git@github.com:bellmyer/shoulda_routing_macros.git

Now here’s an example routing file:

# config/routes.rb
ActionController::Routing::Routes.draw do |map|
  # a simple resource
  map.resources :chickens

  # a resource with extra actions
  map.resources :users, :collection => {:thankyou => :get}, :member => {:profile => :get}

  # nested resources
  map.resources :owners do |owners|
    owners.resources :pets
  end

  # singleton resource
  map.resource :session
  map.ltp_contact_link '/ltp/:code', :controller => 'ltp_contacts', :action => 'new'
end

And this is how easy they are to test:

# test/unit/routing_test.rb
require 'test_helper'

class RoutingTest < ActionController::TestCase
  # simple
  should_map_resources :chickens

  # with extra actions
  should_map_resources :users, :collection => {:thankyou => :get}, :member => {:profile => :get}

  # nested
  should_map_resources :owners
  should_map_nested_resources :owners, :pets

  # singleton
  should_map_resource :session
end

There’s no longer a good excuse to use default routes, or not test your routes in detail. Enjoy!

Restful Controller Tests with Shoulda – Stubbing

January 20, 2010

View the Source Code

This is part 2 of a 5 part series on restful controller tests, using Shoulda as the foundation. Here are all of them so far:

  1. The Basics
  2. Stubbing for Speed

Stubbing

My initial set of controller tests are a great foundation. They lay out exactly how to take advantage of Shoulda to create tests for three different roles of user: anonymous visitor, member, and admin. Now we’ll use the Mocha ruby gem to speed up our tests, by eliminating database calls.

Here’s my (amateurish) video of the changes, followed by a more detailed explanation:

We’ll start by ensuring that rails will require the gem. Add this to config/environments/test.rb:

config.gem 'mocha'

Then on the commandline:

rake gems:install RAILS_ENV=test

Mocha allows us to stub (or “fake”) methods on an object, and track how often they’re called during a test. For example, if you stub an object’s save method in a create action, you can test what happens when saving succeeds (returns true) or fails (returns false), with no messy database interaction.

While I won’t get into a primer on Mocha itself, I will say that almost every database interaction can be removed from functional tests, with the exception of user authentication. I usually leave that in, and that’s my one and only use for fixtures. Fixtures are fast, but cumbersome if you begin adding multiple records for every model in your application. I use factories instead, but they’re not as fast for loading the database. Factories are another chapter in the Functional Tests saga, however.

That said, let’s get on to reviewing the stubbed versions of my tests. Starting with the admin context, our setup changes from this:

    setup do
      @valid = Factory.build(:setting).attributes
      @setting = Factory :setting
      login_as :admin
    end

to this:

    setup do
      @setting = Factory.build :setting
      @setting.id = 1001

      Setting.stubs(:find).returns(@setting)
      Setting.stubs(:find).with(:all, anything).returns([@setting])
      
      login_as :admin
    end

Instead of creating a new setting, we’re using our factory to build an unsaved one. We’re giving it an id since saving would have normally done this. Finally, we’re stubbing out the find method, both for an individual and for all settings, so they return the one we’ve built. Loggin in as admin will be our only database hit.

Now for the actions. The index, show, and new action tests stay exactly the same, except the first two are no longer hitting the database – we’ve stubbed out the find method to automatically return our fake setting. On to the create method, in the “with valid data” context. Here’s the original:

context "with valid data" do
  setup do
    post :create, :setting => @valid
  end
        
  should_assign_to :setting, :class => Setting
  should_redirect_to("setting page"){setting_path(assigns(:setting))}
  should_set_the_flash_to "Setting was successfully created."
        
  should "create the record" do
    assert Setting.find_by_name(@valid['name'])
  end
end

And here’s the stubbed version:

context "with valid data" do
  setup do
    Setting.any_instance.expects(:save).returns(true).once
    Setting.any_instance.stubs(:id).returns(1001)
          
    post :create, :setting => {}
  end

  should_assign_to :setting, :class => Setting
  should_redirect_to("setting page"){setting_path(1001)}
  should_set_the_flash_to "Setting was successfully created."
end

We’ve stubbed any instance of Setting to return true upon save, without actually saving. No database hit, and we still get to test everything. We’ve even dropped our “create the record” test, and stopped passing in valid data, because the expectation we set in the setup handles this. To be more paranoid, we could pass in valid data and check that those params end up in the Setting.new call, but I think that’s overkill since it’s such a basic step.

Next, the “with invalid data” context. First the original:

context "with invalid data" do
  setup do
    post :create, :setting => {}
  end
  
  should_assign_to :setting, :class => Setting
  should_respond_with :success
  should_render_with_layout :settings
  should_render_template :new
  should_not_set_the_flash
end

And the new version:

context "with invalid data" do
  setup do
    Setting.any_instance.expects(:save).returns(false).once
    post :create, :setting => {}
  end
  
  should_assign_to :setting, :class => Setting
  should_respond_with :success
  should_render_with_layout :settings
  should_render_template :new
  should_not_set_the_flash
end

The only changes here are that we’re setting a stubbed expectation of a call to save, forcing a return value of false, and again we don’t need to pass in valid data. Now we can run all the same tests, because we’ve forced the code down the failure branch.

Our edit tests stay the same, with the exception again of no database hit thanks to our stubbed finders. Next up is the update action, and we’ll start with the valid data context. Here’s the original:

context "with valid data" do
  setup do
    put :update, :id => @setting.id, :setting => {:name => 'Bob'}
  end
  
  should_assign_to(:setting){@setting}
  should_redirect_to("setting page"){setting_path(assigns(:setting))}
  should_set_the_flash_to "Setting was successfully updated."
  
  should "update the record" do
    @setting.reload
    assert_equal 'Bob', @setting.name
  end
end

And here’s the new version:

context "with invalid data" do
  setup do
    @setting.expects(:update_attributes).returns(false).once
    put :update, :id => @setting.id, :setting => {}
  end
  
  should_assign_to :setting, :class => Setting
  should_respond_with :success
  should_render_with_layout :settings
  should_render_template :edit
  should_not_set_the_flash
end

Much like a successful create, we stub out our update to succeed and run all the same tests except that the record was actually changed. Now the invalid data context. Here’s the original:

context "with invalid data" do
  setup do
    put :update, :id => @setting.id, :setting => {:name => nil}
  end
  
  should_assign_to :setting, :class => Setting
  should_respond_with :success
  should_render_with_layout :settings
  should_render_template :edit
  should_not_set_the_flash
end

And the new version:

context "with invalid data" do
  setup do
    @setting.expects(:update_attributes).returns(false).once
    put :update, :id => @setting.id, :setting => {}
  end
  
  should_assign_to :setting, :class => Setting
  should_respond_with :success
  should_render_with_layout :settings
  should_render_template :edit
  should_not_set_the_flash
end

And much like our unsuccessful create, we stub out the update to return false, and run the same tests! The finaly action that changes is update, and it’s very easy. First the original:

context "destroying" do
  setup do
    delete :destroy, :id => @setting.id
  end
      
  should_assign_to(:setting){@setting}
  should_redirect_to("index"){settings_path}
  should_not_set_the_flash
      
  should "delete the record" do
    assert !Setting.find_by_id(@setting.id)
  end
end

And the new version:

context "destroying" do
  setup do
    @setting.expects(:destroy).once
    delete :destroy, :id => @setting.id
  end
    
  should_assign_to(:setting){@setting}
  should_redirect_to("index"){settings_path}
  should_not_set_the_flash
end

This time we’re only stubbing out the call to destroy, expecting it to happen once. All the tests stay the same, except we no longer test that a record has actually been removed. No record was actually in the database, so we can’t. Plus, a failing call to destroy is so rare, it doesn’t even let you know in the return value.

The admin context is the only one that changes with these upgrades, members and visitors never get this far in our examples. This will generally make your functional tests over twice as fast, which is huge when you’re faced with mounting test times. I believe I was able to cut testing time down from 90 seconds to just under 30, implementing stubbing across an application’s test suite.

If you view the source code for this part of the series, you’ll see a lot of repetition in the tests. We’ll DRY (Don’t Repeat Yourself) that code in the next chapter in the series.

Shoulda Macro for a Cleaner Uniqueness Test

January 19, 2010

Shoulda macros are so neat and tidy, aren’t they? I love kicking off my unit tests with quick-and-deadly validation and association tests. Here’s an example:

require 'test_helper'

class CouponTest < ActiveSupport::TestCase
  should_validate_presence_of :code, :name, :description

  context "validating uniqueness" do
    setup do
      Factory :coupon
    end

    should_validate_uniqueness_of :code
  end

  should_belong_to :user
end

Do you see anything wrong with this picture? should_validate_uniqueness_of necessarily requires that you already have a record in the database. As much as I hate database interaction in my tests, it’s a necessary evil here. The macro works by copying the attributes of an existing record, and validating it to see if you get any uniqueness errors. So, for this one test, I have to setup a context with a Factory call, because I’m not about to create a record for all the other tests that don’t need it.

Wouldn’t it be nice if you could call should_validate_uniqueness_of and it would create the record for you, if one didn’t already exist? It could use the Factory you’ve already setup. And it would still work the old way if you don’t have factories, or don’t enjoy DRY coding.

Add this macro to your application:

# test/shoulda_macros/validation_macros.rb
module Test
  module Unit
    class TestCase
      class << self
        alias_method :svuo_original, :should_validate_uniqueness_of
        
        def should_validate_uniqueness_of(*attributes)
          class_name = self.name.gsub(/Test$/, '')
          klass = class_name.constantize
          model_sym = class_name.underscore.to_sym
          context "with a record in the database" do
            setup do
              Factory model_sym unless klass.count > 1
            end
            
            svuo_original *attributes
          end
        end
      end
    end
  end
end

Here, we’re overriding the default Shoulda macro. Before we call the original, we’ll setup a context and create the record using our Factory, unless a record is already created. That means it will always “just work”, with the exact same syntax, and all of the options of the original should_validate_uniqueness_of

Thoughtbot can’t create the macro like this by default, because it assumes you have factory_girl installed. But if you do use factories, this upgrade will take some of the ugly out of your tests:

require 'test_helper'

class CouponTest &lt; ActiveSupport::TestCase
  should_validate_presence_of :code, :name, :description
  should_validate_uniqueness_of :code

  should_belong_to :user
end

Nice, eh? As usual, all you need to do to install/create a new shoulda macro is drop a ruby file into the test/shoulda_macros folder, with macro methods defined. You don’t even need to reopen TestCase the way I did, unless you’re overriding an existing macro.

Restful Controller Tests with Shoulda

December 30, 2009

View the Source Code

This is part 1 of a 5 part series on restful controller tests, using Shoulda as the foundation. Here are all of them so far:

  1. The Basics
  2. Stubbing for Speed

After porting eight MVC stacks from an older application to its newer home, and revamping all the tests along the way, I developed a great rhythm in cranking out functional tests for restful resources. I took the time to use the best that modern shoulda has to offer, and came up with (what I feel) is a great set of functional tests.

First, each controller action gets its own context, with several tests executed in each. But wait, there’s more! Do you have multiple roles? I usually have visitor (not logged in), member (logged in, normal user) and admin (logged in, superuser). That means triple the testing, because I want to confirm that the application is behaving the correct way under each circumstance, and the behavior is often very different.

Next, a little background. For authentication this app is using restful_authentication and role_requirement. My basic testing suite consists of shoulda, factory_girl, and woulda. I’m also using autotest, mocha, test_benchmarker, and redgreen to speed up my tests and development cycle, but I’ll cover those in a different article. This is about the basics.

Let’s begin (finally) with a restful controller for site settings – admins have all access, but members and visitors have none. This will allow me to demonstrate how I handle testing each role, and it’s easy to grant more privileges to a role by copying over the tests from say, admin to member.

Admin Tests

I find it’s easier TDD to start with the least restrictive role and work backward, adding restrictions to my controller as I go along. That said, here are my admin tests. I first setup the admin context like so:

  context "as admin" do
    setup do
      @setting = Factory.create(:setting)
      @valid = Factory.build(:setting).attributes
      login_as :admin
    end

    ...
  end

I’ve created a basic setting (using factories), and a hash of valid attributes I can use for create and update actions. I’ve also logged in as admin. If you don’t understand contexts and setup, a context is a group of tests, and the setup for that context is done before every test within. Contexts can also be nested – and they will be.

Index

  context "getting index" do
    setup do
      get :index
    end
      
    should_assign_to(:settings){[@setting]}
    should_respond_with :success
    should_render_with_layout
    should_render_template :index
    should_not_set_the_flash
  end

Using shoulda’s awesome macros, I setup a simple get :index which will run before each shoulda macro, then go nuts testing that @settings is assigned an array with my one and only existing setting. The page responds with success, it renders the default layout with the index template, and doesn’t set the flash. These five tests are what you should think about for every action, under every role. This is the foundation, and you add stuff from here as you add more complicated functionality to the basic scaffold.

New

  context "getting new" do
    setup do
      get :new
    end
      
    should_assign_to :setting, :class =&gt; Setting
    should_respond_with :success
    should_render_with_layout
    should_render_template :new
    should_not_set_the_flash
  end

This is very similar to index above. Now, I’m testing that @setting (singular) is set, with a single object of class Setting. This is as specific as I can test right now, because this was a newly created object in the action – so I can’t test that a specific setting was called. The rest is nearly identical to the index action, expect the template I expect to be rendered.

Create

  context "posting create" do
    context "with valid data" do
      setup do
        post :create, :setting =&gt; @valid.merge('name' =&gt; 'Slappy')
      end
        
      should_assign_to :setting, :class =&gt; Setting
      should_redirect_to("index page"){settings_path}
      should_set_the_flash_to "Your setting was successfully saved."

      should "create the record" do
        assert Setting.find_by_name('Slappy')
      end
    end
      
    context "without valid data" do
      setup do
        post :create, :setting =&gt; {}
      end
        
      should_assign_to :setting, :class =&gt; Setting
      should_respond_with :success
      should_render_with_layout
      should_render_template :new
      should_not_set_the_flash
    end
  end

Now we’re getting a little more complex. We have two outcomes to think about: a successful create, and a failed one. We have to react differently, so we have to test for both.

The first sub-context tests for success. We’re passing in a valid hash of attributes. Notice our tests look different this time – and easier on the fingers. Instead of 5 basic tests, we have 3. We still check that a Setting object was created/assigned, but instead of checking response codes and rendered content, we’re expecting the action to redirect to the index page. And this time, we are expecting a flash message to be set. Finally, I’m checking that the record was actually created. I forced the name attribute to be ‘Slappy’ because no matter how my test suite changes in the future, that name is unlikely to already be in the database.

The next sub-context tests for failure – most likely from invalid input on the part of the user. It happens. We’ve passed an empty attribute list to the action, which should ensure failure if we have at least one required attribute with validates_presence_of in the model. In my next article I’ll cover mocks and stubs for a cleaner way. Our tests are back to the familiar – a setting object should be assigned, and we should expect an HTTP response of success. Our create wasn’t successful, but that’s not what we’re testing here. We’re testing the HTTP response, and success indicates there wasn’t an error at the HTTP level. Next, we’re rendering the new template again, for the user’s second try. We’re not setting the flash though, because you’re probably going to list off the individual errors anyway.

Edit

  context "getting edit" do
    setup do
      get :edit, :id =&gt; @setting.id
    end
    
    should_assign_to(:setting){@setting}
    should_respond_with :success
    should_render_with_layout
    should_render_template :edit
    should_not_set_the_flash
  end

This is back to the short-and-sweet familiar. This is almost like our new action, with a couple exceptions. First, we can now specify (by passing a block to the should_assign_to macro) the specific setting we’re expecting, because we specified it in our request. Also, we’re expecting the edit template to be rendered.

Update

  context "putting update" do
    context "with valid data" do
      setup do
        put :update, :id =&gt; @setting.id, :setting =&gt; {:name =&gt; 'Slappy'}
      end
      
      should_assign_to(:setting){@setting}
      should_redirect_to("index page"){settings_path}
      should_set_the_flash_to 'Your knowledge base was successfully updated.'

      should "update the record"
        assert Setting.find_by_name('Slappy')
      end
    end
    
    context "with invalid data" do
      setup do
        put :update, :id =&gt; @setting.id, :setting =&gt; {:name =&gt; ""}
      end
      
      should_assign_to(:setting){@setting}
      should_respond_with :success
      should_render_with_layout
      should_render_template :edit
      should_not_set_the_flash
    end
  end

You can see we’re doing the same things here that we did with our create action. When calling update however, we can pass only the attributes we want to change. I check that it *has* changed, too.

We have to pass a hash with a piece of bad data to get a failure, and in this case just assume that name is an attribute that can’t be blank.

Destroy

  context "deleting" do
    setup do
      delete :destroy, :id =&gt; @setting.id
    end
    
    should_assign_to(:setting){@setting}
    should_redirect_to("index page"){settings_path}
    should_set_the_flash_to "Your knowledge base was successfully deleted."
    
    should "delete the record" do
      assert !Setting.find_by_id(@setting.id)
    end

This is pretty self-explanatory. We’re assigning, redirecting, and spitting out a nice flash message. We’re also doing a hard check to make sure the setting object is gone.

Members

From here, the tests get a lot easier to absorb. I’ll list the whole member context here:

context "as a member" do
  setup do
    login_as :quentin
  end
  
  context "attempting to get index" do
    setup do
      get :index
    end
    
    should_not_assign_to :settings
    should_respond_with 401
    should_render_with_layout false
    should_not_set_the_flash
  end

  context "attempting to get new" do
    setup do
      get :new
    end
    
    should_not_assign_to :setting
    should_respond_with 401
    should_render_with_layout false
    should_not_set_the_flash
  end

  context "attempting to create" do
    setup do
      post :create, :setting =&gt; {}
    end
    
    should_not_assign_to :setting
    should_respond_with 401
    should_render_with_layout false
    should_not_set_the_flash
  end

  context "attempting to get edit" do
    setup do
      get :edit, :id =&gt; 1
    end
    
    should_not_assign_to :setting
    should_respond_with 401
    should_render_with_layout false
    should_not_set_the_flash
  end

  context "attempting to update" do
    setup do
      put :update, :id =&gt; 1, :setting =&gt; {}
    end
    
    should_not_assign_to :setting
    should_respond_with 401
    should_render_with_layout false
    should_not_set_the_flash
  end

  context "attempting to delete" do
    setup do
      delete :destroy, :id =&gt; 1
    end
    
    should_not_assign_to :setting
    should_respond_with 401
    should_render_with_layout false
    should_not_set_the_flash
  end
end

The only setup I need is to login as a regular user, in this case the default “quentin” that is created by restful_authentication. Notice every action has basically the same four tests: nothing is assigned, a 401 (unauthorized) HTTP error is returned, no layout is rendered, and no flash is set. In my action calls, I can use fake id numbers and empty attribute hashes, because they’ll never be checked anyway. So, no need to create an actual setting object.

By default, role_requirement shows a blank page, but I changed that to display a special 401.html file I created. This is a person who is already logged in, so we know who they are (authentication) but does not have permission to be where they are (authorization).

Visitors

context "as a visitor" do
  context "attempting to get index" do
    setup do
      get :index
    end
    
    should_not_assign_to :settings
    should_redirect_to("login"){new_session_path}
    should_set_the_flash_to "You need to login to do this."
  end

  context "attempting to get new" do
    setup do
      get :new
    end
    
    should_not_assign_to :setting
    should_redirect_to("login"){new_session_path}
    should_set_the_flash_to "You need to login to do this."
  end

  context "attempting to create" do
    setup do
      post :create, :setting =&gt; {}
    end
    
    should_not_assign_to :setting
    should_redirect_to("login"){new_session_path}
    should_set_the_flash_to "You need to login to do this."
  end

  context "attempting to get edit" do
    setup do
      get :edit, :id =&gt; 1
    end
    
    should_not_assign_to :setting
    should_redirect_to("login"){new_session_path}
    should_set_the_flash_to "You need to login to do this."
  end

  context "attempting to update" do
    setup do
      put :update, :id =&gt; 1, :setting =&gt; {}
    end
    
    should_not_assign_to :setting
    should_redirect_to("login"){new_session_path}
    should_set_the_flash_to "You need to login to do this."
  end

  context "attempting to delete" do
    setup do
      delete :destroy, :id =&gt; 1
    end
    
    should_not_assign_to :setting
    should_redirect_to("login"){new_session_path}
    should_set_the_flash_to "You need to login to do this."
  end
end

Visitors are different from members without access. We want to be nicer and assume visitors just aren’t logged in yet, if they try to access protected actions. So we simply redirect them to the login page with a nice message. Just as with members, there’s no need to create actual objects in the database, or give actual id numbers or attribute hashes, since the action will never get far enough to check.

Conclusion

Once I developed this basic set of tests, I was pleased with how consistent my controller coding became. I don’t have a generator for these, I code them by hand every time. It keeps me sharp, and doesn’t take that long when you get into the swing of it. This isn’t the final draft, however. I’ve since modified this template with mocking and stubbing to greatly improve the speed, cutting runtime to a quarter of what it was. I’ll write about that approach in my next article.

Files

Here are Pastie links to my sample controller test and controller files. My controller is mostly vanilla scaffold with all the format junk stripped out, and a couple of key protected methods at the end.

settings_controller_test.rb
settings_controller.rb

Setting the flash in tests

December 27, 2009

I recently needed to test a thankyou page for a form. The previous action sets a flash value, flash[:email], and then redirects to this page. Testing the page by itself requires that this flash be set. Here’s how to do it:

get :thankyou, {}, nil, {:email =&gt; 'test@test.com'}

This will ensure that when the page is called, it has the correct flash elements set. This is important because we’re calling the page directly, not redirecting from a previous action that would have set the flash automatically.

The test methods to call actions (get, post, put, delete) actually take four parameters, even though we typically only use two. The first is the action, and the second is the parameter list. We normally don’t put curly braces around the parameter list because Ruby is smart enough to know that if it sees a bunch of key-value pairs at the end of a method call, it should scoop them up into a single hash. We can’t do that for our example because parameter, session, and flash data need to be passed in different hashes.

For more details visit the same page I did, A Guide to Testing Rails Applications.

undefined local variable or method `should_act_as_list’

December 26, 2009

I got this error today, which was a surprise because I was porting over good, tested code from an old version of an application. To recreate the error after solving, I built a simple test app with a single model, SantasList. The test file looks like this:

require 'test_helper'

class SantasListTest < ActiveSupport::TestCase
  should_act_as_list
end

At first I suspected a missing gem, but environments/test.rb showed this:

  config.gem 'thoughtbot-shoulda', :lib => 'shoulda', :source => 'http://gems.github.com'
  config.gem 'seanhussey-woulda', :lib => 'woulda', :source => 'http://gems.github.com'

I half suspected that woulda wasn’t loading properly, and I was half right. I started to trace the problem in the gem itself, which I’d wisely unpacked into the application. Inside the woulda gem, I found the contents of shoulda_macros/woulda_macros.rb:

require 'woulda'

Simple enough. So in lib/woulda.rb I found the line that loads the acts_as_list macro:

  require "#{woulda_dir}/acts_as_list" if defined? ActiveRecord::Acts::List

Voila!* First, looking at the whole file shows that the author was smart, and only loads the macros that might actually be used. Second, I’ve found my problem: I don’t have the acts_as_list plugin installed! No plugin, no macro. So I was half right – the macro wasn’t loading, but it was my fault. As I said, I’m porting the application to a new code base, and I hadn’t needed this plugin yet. I’m thrilled the problem was so easy to find, and Sean Hussey’s clean code certainly helped.

As a side pitch, if you’re not using woulda yet, I highly recommend it. For shoulda users, it provides macros for the most common ActiveRecord plugins like acts_as_list, acts_as_taggable, will_paginate, paperclip, acts_as_state_machine, and more. And if you’re not familiar with shoulda macros, then why not??

*I like to say “viola” as a joke, but in print it just looks like a typo.

Running your Rails Test Database in Memory (RAM)

August 4, 2009

I recently read a blog post by Amr Mostafa that benchmarked running MySQL databases in memory. I’ve been trying to figure out how to do this, and he had the answer: use the tmpfs filesystem, which runs in memory, to store your database.  I’ll have to figure out just how difficult that is later, since I’m not a super DBA…or even really a DBA at all.

Amr is not a Rails developer, and the purpose of his benchmark was to simulate regular web traffic.  His results seemed ambiguous, but I noticed something missing in his trial: writes to the database.  His benchmark tests only used select statements, which read from the database.  While this is the majority of most database usage, I think the perfomance gain during writes would tip the scales decidedly in favor of running MySQL in memory, if you can afford the RAM.

This has an added benefit to us Ruby on Rails developers: we could potentially use it to dramatically increase the speed of our Test Driven Development, especially for those of use (should be all of us) using autotest!  TDD requires running tests every few minutes, or even seconds.  And writing to the database in tests is a lot bigger piece of the puzzle, since the database is recreated from scratch before every test.

For a lot of us, test suites are manageable.  Autotest only runs tests for changed files during normal development, occasionally running the entire test suite.  This, coupled with judicious mocking, stubbing and unit testing techniques can keep most test suites under control.  But larger apps use increasingly more tests, and higher-level tools like RSpec can be especially resource-intensive.

I tried to contact Mostafa about running some benchmarks for write speed, but my comment was considered spam!  I did get a smaller message through, so hopefully I’ll hear back.  If so, I’ll post a link to his thoughts/results.  Until then, I may dabble with doing this myself, and seeing what amateurish benchmarks I can run myself.


Follow

Get every new post delivered to your Inbox.