Tuesday, April 20, 2010

Taking over the Remarkable gem

Remarkable is an awesome gem. If you use Rspec for a Rails project, this macro framework is certainly something to consider.

The classic way of using Rspec to test your ActiveRecord model looks like this:

class Person < ActiveRecord::Base
belongs_to :organization

validates_presence_of :name
validates_presence of :email
validates_uniqueness_of :email
end

# In spec/models/person_spec.rb
describe Person do
before(:each) do
@valid_attributes = {
:name => "Name"
:email => "email@example.com"
}
end

it "should create a new instance given valid attributes" do
Person.create!(@valid_attributes)
end

it "should validate presence of :name" do
@person = Person.new(@valid_attributes.except(:name)
@person.should_not be_valid
@person.errors.on(:name).should_not be_nil
end

it "should validate presence of :email" do
@person = Person.new(@valid_attributes.except(:email)
@person.should_not be_valid
@person.errors.on(:email).should_not be_nil
end

it "should validate uniqueness of :email" do
@original_person = Person.create!(@valid_attributes)
@clone = Person.create!(@valid_attributes)
@person.should_not be_valid
@person.errors.on(:email).should_not be_nil
end
end

[ See Gist ]

What this does is create a template model that has all the required attributes. We have an example that makes sure it is valid. Then for every validation, we create a modified attribute set so we can test each attribute in isolation.

This gets old very fast. Trust me, I've written many tests like this since Rspec got popular in 2008.

So how can we make this easier?

We can try using fixtures. The problem with fixtures is that (1) we have to manage our own primary keys and argue with the database and (2) you would have to load the fixture in, create a clone, and attempt to test each of the validation.

We can try mocking. This would give us the isolation when we stub out the mock model, but that's essentially another way of writing a valid_attributes. Besides, now we're digging deep into the internals of ActiveRecord.

We can use factories. I like factories, in particular, I currently use the Machinist in combination with Faker gem and Forgery. This gives me the advantage of having a blueprints.rb file I can include in db/seeds.rb to generate data I want to drop into my views. However, we are still essentially testing the ActiveRecord framework.

This is where Remarkable comes in. The above Rspec condenses down to:

describe Person do
it { should validate_presence_of :name }
it { should validate_presence_of :email }
it { should validate_uniqueness_of :email }
end

Done.

[ See Gist]

Notice that these macros shadow the ActiveRecord declarations. This is true for all the other macros, including association macros such as should have_many, should belong_to. Instead of focusing on boilerplate test examples, you focus on writing examples for custom code and really weird business logic.

Remarkable has come a long way from its beginning as a "Shoulda for Rspec". Since version 3.0, Remarkable has been refactored into a generic framework for creating Rspec macros. Remarkable::ActiveRecord and Remarkable::Rails were extensions of this macro. As a result, we saw some other macros: a remarkable for paperclip, mongo, and datamapper. There is also an internationalization package that lets you internationalize your rspec.

However, with big refactoring of Rails 3, followed by the big refactoring of Rspec 2, Remarkable needs some work. Carlos Brando, the founding author of the Remarkable gem has been looking for a new maintainer, and has passed the torch to me. As I stated in the Remarkable mailing list and on Rails-talk, my plan is to release a Remarkable 4.0 that is compatible with Rails 3 and Rspec 2, however, it will not be backwards compatible with Rails 2 and Rspec 1. Remarkable 3.3 will be around for that.

If you use Remarkable and plan on using it for your Rails 3 project, please speak up now. We're going to take advantage of the mass refactoring to do some refactoring of our own.

2 comments:

  1. Hey there! I am wondering whether you've made progress with Remarkable. If not, I am interested in participating in the refactoring. I am a newbie Ruby/Rails program, but have lots of interest in the language, as well making an impact in the community. I've done some research on Remarkable vs. Shoulda, and see that Remarkable is win. My past includes lots of PHP (w. Symfony framework). I am also very proficient in AS3 (Flash/Flex). So, while I am lacking in my knowledge of Ruby, I am not a newb to dev work. Looking forward to joining the project.

    Cheers,
    Alex

    ReplyDelete
  2. Hey, I was looking to use remarkable for a rails 3 project and I found that remarkable-4.0 is still in alpha. I am quite experienced with rails and would like to help to take it forward. Where do I start?

    ReplyDelete