Carlos Brando

Nome do Jogo

Remarkable 3.0 is out and it's... well... remarkable!

Wow, a major release? Yes, a major release and we have plenty reasons for that. Since me and José Valim were working on it for a couple months, we have quite a list :). So follow up:

1. Remarkable Core

Remarkable is now a framework for rspec matchers. Since Rails matchers are not simple, as Remarkable grew, we saw our matchers full of logic to show messages (description and failure messages) and that some methods could be automatically generated, reducing repetitive work.

Compare the allow_mass_assignment_of matcher in Remarkable 2.x and in Remarkable 3.0. Which one do you prefer? :)

So we built a DSL and called it Remarkable, which with Remarkable ActiveRecord and Remarkable Rails, provides you nice matchers/macros to speed up your tests. To install them, just do:

sudo gem install remarkable_rails

You can also install just remarkable or just remarkable_activerecord. But remarkable_rails brings you the whole packet.

2. I18n

On Rails Summit Latin America, Obie Fernandez talked about the Hash Rocket way and one of the items were that they actually print out the specs and give it to the client. Both me and José work with Rails projects and we couldn’t deliver the same to technical clients, because the output was in english. Well, we scratched our own itch!

This is how an English output would be like:

Example disabled: require age to be set

User
- should require name and email to be set
- should ensure length of name is within 3..40 characters
- should require unique values for email
- should ensure numericality of age allowing only integer values and allowing blank values

In Remarkable, we can have the same results in Portuguese:

Exemplo desativado: exigir presença de idade Usuário - deve exigir presença de nome e email - deve garantir tamanho de nome seja entre 3..40 caracteres - deve garantir valores únicos para email - deve aceitar apenas números em idade permitindo somente valores inteiros e permitindo valores nulos

As you have noticed everything gets translated including the class and attributes names. This happens by setting up Remarkable and Rails yml files.

3. Macros

In Remarkable earlier versions, we coded the matcher (validate_presence_of) but most of the time we had to do some tweaks in other to a matcher becomes a macro (should_validate_presence_of). In Remarkable 3.0 it happens transparently.

In other words, if you are using Remarkable 3.0 to build your matchers you have cleaner matchers, I18n support and transparent macros creation.

4. Pending and disabled macros

In Remarkable 2.2, we added the ability to mark macros as disabled:

xshould_validate_uniqueness_of :name

And it would print on your specs output:

Example disabled: require unique values for name

As some people pointed out, Remarkable was still lacking support for pending macros. Pending examples in Rspec would be:

it "should have one manager" do
  pending("create managers resource")
end

it "should validate associated manager" do
  pending("create managers resource")
end

And now in Remarkable, you have the pending group:

pending "create managers resource" do
  should_have_one :manager
  should_validate_associated :manager
end

In both cases a message “PENDING: create managers resources” is appended to matcher description.

5. More options to matchers

In Remarkable 2.2, we started to support ALL Active Record validations with ALL options. And now we extended support for association matchers!

From only :dependent and :through as supported options, now it accepts:

:through, :class_name, :foreign_key, :dependent, :join_table, :uniq, :readonly, :validate, :autosave, :counter_cache, :polymorphic

Besides matchers became much smarter. Whenever :join_table or :through is given as option, it also checks if the given table exists. Whenever :foreign_key or :counter_cache is given, it also checks if the given column exists.

6. Controllers matchers extended

To provide I18n, we ported redirect_to and render_template matchers from rspec-rails. We also added some extra options to respond_with and render template, so you can now do:

render_template 'edit', :layout => 'users'

respond_with 422, :content_type => Mime::XML,
                  :body => /Unprocessable Entitity/

And the route matcher is willing to make your life twice easier. Since it tests params recognitation and routes generation at once, you can cut your routing_specs at least in a half! So the following lines:

route_for(:controller => "companies",
          :action => "show",
          :id => "1").should == "/companies/1"

params_from(:get, "/companies/1").should == { :controller => "companies",
                                              :action => "show",
                                              :id => "1" }

Will become:

route(:get, "/companies/1", :controller => "companies",
                            :action     => "show",
                            :id         => "1")

7. Macro stubs

There is a presentation from José Valim that explains this feature with more details, but it basically makes your controllers specs from this:


describe TasksController do
  def mock_task(stubs={})
    @task ||= mock_model(Task, stubs)
  end

  describe "responding to #POST create" do
    it "exposes a newly created task as @task" do
      Task.should_receive(:new).with({'these' => 'params'}).
      and_return(mock_task(:save => true))‏
      post :create, :task => {:these => 'params'}
      assigns[:task].should equal(mock_task)end

    it "redirects to the created task" do
      Task.stub!(:new).and_return(mock_task(:save => true))‏
      post :create, :task => {}
      response.should redirect_to(task_url(mock_task))end
  end
end

Become this:

describe TasksController do
  mock_models :task

  describe :post => :create, :task => {:these => 'params'} do
    expects :new,  :on => Task, :with => { :these => 'params' }, :returns => mock_task
    expects :save, :on => mock_task, :returns => true

    should_assign_to :task, :with => mock_task
    should_redirect_to { task_url(mock_task) }
  end
end

It aims to improve readability and be more DRY, since you declare your expectations/stubs just once. You can see the whole controller specs in rspec and remarkable way compared here.

It works by eval’ing the expectation/stubs chain and performing the action before each macro is executed. On should_assign_to it evals using expectations (:should_receive), on should_redirect_to it uses stubs (:stub!).

We also want to thank David Chelimsky that gave the nice hint to allow :post => :create inside the describe method and suggested to use mocha syntax. :)

8. Documentation, documentation and documentation

Yes, three times, because each project is very well documented. Browse them:

Remarkable: http://remarkable.rubyforge.org/core/

Remarkable ActiveRecord: http://remarkable.rubyforge.org/activerecord/ http://remarkable.rubyforge.org/activerecord/classes/Remarkable/ActiveRecord/Matchers.html (just matchers)

Remarkable Rails: http://remarkable.rubyforge.org/rails/ http://remarkable.rubyforge.org/rails/classes/Remarkable/ActionController/Matchers.html (just matchers)

9. Volunteers?

Last but definitely not least: we need you!

http://travelpod.files.wordpress.com/2009/02/446px-uncle_sam_pointing_finger.jpg

We created a solid basis for matchers/macros creation. And we want to take it further by hosting even more matchers. So anyone who wants to work on Remarkable Datamapper, Remarkable Sequel, Remarkable Sinatra, please step in! We are waiting for you. :)

And all the others can equally help us by joining the group and posting bugs, matchers, enhancements in bug tracking system.

Comments